Files
Genarrative/docs/technical/WECHAT_LOGIN_REAL_INTEGRATION_RUNBOOK_2026-04-21.md

11 KiB
Raw Permalink Blame History

微信登录真实联调手册

日期:2026-04-21

1. 文档目的

这份文档用于把当前仓库里的微信登录,从“代码已具备 mock / real 双模式”推进到“开发、测试、部署都能按步骤联调”的级别。

文档目标不是重复实现设计,而是回答以下落地问题:

  1. 本地先怎么用 mock 跑通
  2. 什么时候切 real
  3. 真实微信开放平台需要配什么
  4. 回调地址到底怎么拼
  5. 前后端各自怎么验证
  6. 失败时先查哪一层

2. 当前实现结论

当前仓库里的微信登录实现固定为两段式:

  1. 微信 OAuth 只负责返回第三方身份:code -> openid / unionid
  2. Rust api-server 负责把该第三方身份换成本系统 JWT

因此当前正式口径固定为:

  1. 前端不直接消费微信 token
  2. SpacetimeDB 未来也不直接消费微信 token
  3. 统一由 api-server 签发系统 JWT再回给前端和后续服务层

3. 模式说明

3.1 mock 模式

适用场景:

  1. 本地前后端先验证交互链路
  2. 还没有微信开放平台配置
  3. 还没有可用公网回调域名

当前配置:

WECHAT_AUTH_ENABLED="true"
WECHAT_AUTH_PROVIDER="mock"
WECHAT_CALLBACK_PATH="/api/auth/wechat/callback"
WECHAT_REDIRECT_PATH="/"
WECHAT_MOCK_USER_ID="wx-mock-user"
WECHAT_MOCK_UNION_ID="wx-mock-union"
WECHAT_MOCK_DISPLAY_NAME="微信旅人"
WECHAT_MOCK_AVATAR_URL=""

mock 模式行为固定为:

  1. GET /api/auth/wechat/start 仍会创建一次性 state
  2. 返回的 authorizationUrl 不会跳去微信,而是直接指向本地 callback
  3. callback 会走 mock 身份:mock_code + state
  4. 后端仍会完整创建微信登录态、refresh session 与待绑定账号

3.2 real 模式

适用场景:

  1. 已拿到微信开放平台应用
  2. 已有可访问的公网域名
  3. 已在微信开放平台后台配置回调域名

当前配置:

WECHAT_AUTH_ENABLED="true"
WECHAT_AUTH_PROVIDER="real"
WECHAT_APP_ID="你的微信开放平台 AppID"
WECHAT_APP_SECRET="你的微信开放平台 AppSecret"
WECHAT_CALLBACK_PATH="/api/auth/wechat/callback"
WECHAT_REDIRECT_PATH="/"

real 模式行为固定为:

  1. GET /api/auth/wechat/start 返回微信真实授权地址
  2. 用户在微信侧完成授权
  3. 微信回跳到 WECHAT_CALLBACK_PATH
  4. 后端用 codeaccess_token/openid/unionid
  5. 后端签发本系统 JWT并把结果通过 URL hash 回跳给前端

4. 环境变量清单

当前真实联调至少需要这些变量:

变量 必填 说明
WECHAT_AUTH_ENABLED 是否启用微信登录
WECHAT_AUTH_PROVIDER mockreal
WECHAT_APP_ID real 模式必填 微信开放平台应用 ID
WECHAT_APP_SECRET real 模式必填 微信开放平台应用 Secret
WECHAT_CALLBACK_PATH 回调路径,默认 /api/auth/wechat/callback
WECHAT_REDIRECT_PATH 登录完成后前端默认落点
WECHAT_AUTHORIZE_ENDPOINT 默认桌面二维码授权地址
WECHAT_ACCESS_TOKEN_ENDPOINT 默认 access_token 接口
WECHAT_USER_INFO_ENDPOINT 默认用户信息接口
WECHAT_JS_CODE_SESSION_ENDPOINT 默认小程序 jscode2session 接口
WECHAT_MINI_PROGRAM_APP_ID 小程序 real 模式必填 微信小程序 AppID不填时回退 WECHAT_APP_ID
WECHAT_MINI_PROGRAM_APP_SECRET 小程序 real 模式必填 微信小程序 AppSecret不填时回退 WECHAT_APP_SECRET
WECHAT_STATE_TTL_MINUTES state 有效期,默认 15 分钟

补充说明:

  1. WECHAT_CALLBACK_PATH 只是路径,不是完整 URL。
  2. 当前完整回调地址由后端在运行时按请求头拼接:
    • 优先 x-forwarded-proto
    • 其次 host / x-forwarded-host
  3. 因此部署到反向代理后,代理层必须正确透传 hostx-forwarded-proto

5. 微信开放平台后台配置

真实联调前,需要先在微信开放平台完成以下配置:

  1. 创建网站应用并拿到 AppID / AppSecret
  2. 配置网站授权回调域名
  3. 确保回调域名与实际访问域名一致

当前项目必须特别注意:

  1. 微信后台通常配置的是“域名”,不是完整路径
  2. 但项目真正收到回调时会落到:
https://你的域名/api/auth/wechat/callback
  1. 如果代理层把 HTTPS 终止在上游网关,必须把 x-forwarded-proto=https 正确传给 api-server
  2. 否则后端可能生成 http://.../api/auth/wechat/callback,导致微信侧回调地址不匹配

6. 本地 mock 联调步骤

6.1 配置

.env.local 中写入:

SMS_AUTH_ENABLED="true"
WECHAT_AUTH_ENABLED="true"
WECHAT_AUTH_PROVIDER="mock"
VITE_AUTH_ALLOW_DEV_GUEST="false"

6.2 启动

前端和后端分别启动当前项目自己的开发链路。

若只验证 Rust 后端,可直接启动 server-rsapi-server

6.3 验证顺序

  1. 请求 GET /api/auth/login-options
  2. 期望返回同时包含:
    • phone
    • wechat
  3. 前端点击“微信登录”
  4. 浏览器应先跳到 /api/auth/wechat/start
  5. 随后直接命中本地 /api/auth/wechat/callback?...
  6. 前端收到 hash
    • auth_provider=wechat
    • auth_token=...
    • auth_binding_status=pending_bind_phone
  7. 页面进入“绑定手机号”界面
  8. 发送验证码并绑定手机号
  9. 若手机号未使用,当前账号激活
  10. 若手机号已对应正式账号,微信身份并入已有正式账号

7. 真实微信联调步骤

7.1 切换配置

.env.local 中切到:

SMS_AUTH_ENABLED="true"
WECHAT_AUTH_ENABLED="true"
WECHAT_AUTH_PROVIDER="real"
WECHAT_APP_ID="你的 AppID"
WECHAT_APP_SECRET="你的 AppSecret"
WECHAT_CALLBACK_PATH="/api/auth/wechat/callback"
WECHAT_REDIRECT_PATH="/"
VITE_AUTH_ALLOW_DEV_GUEST="false"

7.2 部署要求

需要有一个用户浏览器能够访问的地址,例如:

https://game.example.com

并且该地址最终把认证请求转发到当前 Rust api-server

7.3 代理层要求

反向代理必须至少透传:

  1. Host
  2. X-Forwarded-Proto
  3. X-Forwarded-Host(如果你的代理链路会改写 Host

7.4 验证顺序

  1. 浏览器访问前端页面
  2. 点击“微信登录”
  3. 观察 /api/auth/wechat/start 返回的 authorizationUrl
  4. 确认其中 redirect_uri 指向真实回调地址
  5. 在微信授权完成后,确认请求回到:
/api/auth/wechat/callback?code=...&state=...
  1. 观察回跳前端地址是否带上:
    • auth_provider=wechat
    • auth_token=...
    • auth_binding_status=active|pending_bind_phone
  2. 如果进入待绑定页面,继续完成手机号绑定
  3. 绑定后再请求 GET /api/auth/me
  4. 确认:
    • 当前用户存在
    • wechatBound = true
    • bindingStatus 已更新为目标状态

8. 小程序 web-view 登录联调步骤

小程序壳走原生 wx.login,不走网页 OAuth callback。联调前需要额外确认

WECHAT_AUTH_ENABLED=true
WECHAT_AUTH_PROVIDER=real
WECHAT_MINI_PROGRAM_APP_ID="你的微信小程序 AppID"
WECHAT_MINI_PROGRAM_APP_SECRET="你的微信小程序 AppSecret"

miniprogram/config.js 中确认:

const WEB_VIEW_ENTRY_URL = 'https://你的H5业务域名/';
const API_BASE_URL = 'https://你的服务器域名/';
const MINI_PROGRAM_APP_ID = '你的微信小程序 AppID';

联调流程:

  1. 微信开发者工具打开项目根目录。
  2. 小程序启动后调用 wx.login
  3. 小程序壳请求:
POST /api/auth/wechat/miniprogram-login
  1. 后端通过 jscode2session 兑换 openid/unionid
  2. 后端返回系统 tokenbindingStatususer
  3. 小程序壳打开 H5并在 hash 中附加:
    • auth_provider=wechat
    • auth_token=...
    • auth_binding_status=active|pending_bind_phone
  4. H5 消费 hash 后通过 /api/auth/me 恢复登录态。

这里不能把裸 openid 作为 web-view query 登录凭证;openid 只能留在后端身份绑定层H5 只消费本系统 JWT。

9. 账号命中规则

当前实现固定按以下顺序命中已有账号:

  1. 先按 unionid
  2. 再按 openid
  3. 都没有命中时,创建 pending_bind_phone 微信壳账号

补充规则:

  1. 若按 unionid 命中了已有微信身份,但本次微信回调带来了新的 openid,后端会把新的 openid -> user_id 映射补齐
  2. 若后续绑定手机号时发现该手机号已经属于正式账号,则会把微信身份并入这个正式账号

10. 前端验收点

前端联调时至少检查以下行为:

  1. 登录弹窗只在 login-options 返回 wechat 时显示“微信登录”按钮
  2. 点击微信登录后应跳去后端返回的 authorizationUrl
  3. 回调 hash 被前端消费后,应把 auth_token 存入本地登录态
  4. auth_binding_status=pending_bind_phone,页面必须进入绑定手机号界面
  5. 绑定成功后,应切回正常已登录状态

10. 后端验收点

当前后端至少应满足以下检查:

  1. GET /api/auth/wechat/start 能返回授权地址
  2. GET /api/auth/wechat/callback 能创建系统会话并回跳
  3. POST /api/auth/wechat/bind-phone 能完成补绑
  4. GET /api/auth/me 能反映最新 bindingStatus / wechatBound
  5. access token claims 中的 provider 应保持当前会话来源为 wechat

11. 常见失败点

11.1 点微信登录后直接报“微信登录暂未启用”

先检查:

  1. WECHAT_AUTH_ENABLED 是否为 true
  2. 运行的是否真是 Rust api-server
  3. 前端是否还在打旧 Node 服务

11.2 微信授权页能打开,但回调报错

先检查:

  1. 微信后台配置的回调域名是否正确
  2. 代理层是否正确透传 hostx-forwarded-proto
  3. WECHAT_CALLBACK_PATH 是否与当前代码路径一致

11.3 按 unionid 命中后又出现新壳账号

先检查:

  1. 微信开放平台当前应用是否真的能返回稳定 unionid
  2. 当前账号历史上是否只有 openid 没有 unionid
  3. 是否发生了不同开放平台主体之间的数据割裂

11.4 绑定手机号后命中了已有正式账号,但前端看到 loginMethod=phone

这是当前实现允许的结果。

原因是:

  1. 返回的是目标正式账号快照
  2. 目标正式账号本身的主登录方式仍可能是 phone
  3. 但当前会话签发的 access token provider 仍然是 wechat

12. 当前自动化验证证据

当前仓库里已经有这些自动化验证:

  1. cargo test -p api-server 覆盖:
    • wechat/start
    • wechat/callback
    • wechat/bind-phone
  2. cargo test -p module-auth 覆盖:
    • unionid 优先命中已有微信用户
    • 微信待绑定账号并入已有手机号正式账号
  3. npm test -- --run src/services/authService.test.ts 覆盖:
    • 前端微信登录起跳
    • callback hash 消费

13. 一句话切换原则

本地先用 mock 跑通页面和会话闭环,公网域名与微信后台配置就绪后,再切 real 做真实 OAuth 联调。