feat: add wechat miniprogram webview login
This commit is contained in:
@@ -92,6 +92,25 @@
|
||||
2. 若是并入已有手机号正式账号,则返回目标正式账号快照,当前实现会保持其账号主登录方式,例如 `loginMethod = phone`。
|
||||
3. 但 access token 中的 `provider` 仍按**本次登录来源**签发,不依赖账号主登录方式推断,因此微信绑定后的当前会话仍会签发 `provider = wechat`。
|
||||
|
||||
### 3.4 `POST /api/auth/wechat/miniprogram-login`
|
||||
|
||||
职责固定为:
|
||||
|
||||
1. 接收微信小程序原生壳通过 `wx.login` 拿到的 `code`。
|
||||
2. 在 `Axum` 内调用微信 `jscode2session`,兑换 `openid/unionid`。
|
||||
3. 复用 `resolve_login` 处理 `unionid/openid -> user_id` 的查找、补写和待绑定账号创建。
|
||||
4. 签发本系统 access token,并创建 refresh session。
|
||||
5. 返回:
|
||||
- `token`
|
||||
- `bindingStatus`
|
||||
- `user`
|
||||
|
||||
关键约束:
|
||||
|
||||
1. 小程序壳不能把裸 `openid` 直接拼给 H5 做登录。
|
||||
2. H5 仍只消费本系统 `auth_token`,小程序壳只是把这枚 token 放入既有 hash 回调格式。
|
||||
3. 小程序请求必须补传 `x-client-type=mini_program` 与 `x-client-runtime=wechat_mini_program`,用于 refresh session 记录来源。
|
||||
|
||||
## 4. 当前最小实现策略
|
||||
|
||||
当前阶段为了先打通 Rust 后端闭环,采用以下最小实现:
|
||||
@@ -125,6 +144,7 @@
|
||||
4. `GET /api/auth/wechat/start`
|
||||
5. `GET /api/auth/wechat/callback`
|
||||
6. `POST /api/auth/wechat/bind-phone`
|
||||
7. `POST /api/auth/wechat/miniprogram-login`
|
||||
|
||||
## 6. 环境变量
|
||||
|
||||
@@ -139,11 +159,14 @@
|
||||
7. `WECHAT_AUTHORIZE_ENDPOINT`
|
||||
8. `WECHAT_ACCESS_TOKEN_ENDPOINT`
|
||||
9. `WECHAT_USER_INFO_ENDPOINT`
|
||||
10. `WECHAT_STATE_TTL_MINUTES`
|
||||
11. `WECHAT_MOCK_USER_ID`
|
||||
12. `WECHAT_MOCK_UNION_ID`
|
||||
13. `WECHAT_MOCK_DISPLAY_NAME`
|
||||
14. `WECHAT_MOCK_AVATAR_URL`
|
||||
10. `WECHAT_JS_CODE_SESSION_ENDPOINT`
|
||||
11. `WECHAT_MINI_PROGRAM_APP_ID`
|
||||
12. `WECHAT_MINI_PROGRAM_APP_SECRET`
|
||||
13. `WECHAT_STATE_TTL_MINUTES`
|
||||
14. `WECHAT_MOCK_USER_ID`
|
||||
15. `WECHAT_MOCK_UNION_ID`
|
||||
16. `WECHAT_MOCK_DISPLAY_NAME`
|
||||
17. `WECHAT_MOCK_AVATAR_URL`
|
||||
|
||||
## 7. 与后续 SpacetimeDB 的衔接要求
|
||||
|
||||
|
||||
@@ -100,6 +100,9 @@ real 模式行为固定为:
|
||||
| `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` 分钟 |
|
||||
|
||||
补充说明:
|
||||
@@ -225,7 +228,46 @@ https://game.example.com
|
||||
- `wechatBound = true`
|
||||
- `bindingStatus` 已更新为目标状态
|
||||
|
||||
## 8. 账号命中规则
|
||||
## 8. 小程序 web-view 登录联调步骤
|
||||
|
||||
小程序壳走原生 `wx.login`,不走网页 OAuth callback。联调前需要额外确认:
|
||||
|
||||
```bash
|
||||
WECHAT_AUTH_ENABLED=true
|
||||
WECHAT_AUTH_PROVIDER=real
|
||||
WECHAT_MINI_PROGRAM_APP_ID="你的微信小程序 AppID"
|
||||
WECHAT_MINI_PROGRAM_APP_SECRET="你的微信小程序 AppSecret"
|
||||
```
|
||||
|
||||
在 `miniprogram/config.js` 中确认:
|
||||
|
||||
```js
|
||||
const WEB_VIEW_ENTRY_URL = 'https://你的H5业务域名/';
|
||||
const API_BASE_URL = 'https://你的服务器域名/';
|
||||
const MINI_PROGRAM_APP_ID = '你的微信小程序 AppID';
|
||||
```
|
||||
|
||||
联调流程:
|
||||
|
||||
1. 微信开发者工具打开项目根目录。
|
||||
2. 小程序启动后调用 `wx.login`。
|
||||
3. 小程序壳请求:
|
||||
|
||||
```http
|
||||
POST /api/auth/wechat/miniprogram-login
|
||||
```
|
||||
|
||||
4. 后端通过 `jscode2session` 兑换 `openid/unionid`。
|
||||
5. 后端返回系统 `token`、`bindingStatus` 与 `user`。
|
||||
6. 小程序壳打开 H5,并在 hash 中附加:
|
||||
- `auth_provider=wechat`
|
||||
- `auth_token=...`
|
||||
- `auth_binding_status=active|pending_bind_phone`
|
||||
7. H5 消费 hash 后通过 `/api/auth/me` 恢复登录态。
|
||||
|
||||
这里不能把裸 `openid` 作为 web-view query 登录凭证;`openid` 只能留在后端身份绑定层,H5 只消费本系统 JWT。
|
||||
|
||||
## 9. 账号命中规则
|
||||
|
||||
当前实现固定按以下顺序命中已有账号:
|
||||
|
||||
@@ -238,7 +280,7 @@ https://game.example.com
|
||||
1. 若按 `unionid` 命中了已有微信身份,但本次微信回调带来了新的 `openid`,后端会把新的 `openid -> user_id` 映射补齐
|
||||
2. 若后续绑定手机号时发现该手机号已经属于正式账号,则会把微信身份并入这个正式账号
|
||||
|
||||
## 9. 前端验收点
|
||||
## 10. 前端验收点
|
||||
|
||||
前端联调时至少检查以下行为:
|
||||
|
||||
|
||||
@@ -6,11 +6,18 @@
|
||||
|
||||
本次先用微信小程序 `web-view` 承载现有 H5,不重写 React/Vite 主前端,也不把 SpacetimeDB SDK 或业务规则搬进小程序端。
|
||||
|
||||
当前小程序壳只承担三件事:
|
||||
当前小程序壳只承担四件事:
|
||||
|
||||
1. 提供微信开发者工具可识别的 `miniprogram/` 工程根目录。
|
||||
2. 用一个全屏 `web-view` 打开现有 H5 入口。
|
||||
3. 给 H5 URL 附加来源标记,便于后续识别 `wechat_mini_program` 宿主。
|
||||
2. 在原生小程序壳中调用 `wx.login` 获取小程序 `code`。
|
||||
3. 调用服务器域名下的 `/api/auth/wechat/miniprogram-login`,由 Rust `api-server` 兑换微信身份并签发系统登录态。
|
||||
4. 用一个全屏 `web-view` 打开现有 H5 入口,并把系统 `auth_token` 放入 H5 现有登录回调 hash。
|
||||
|
||||
重要边界:
|
||||
|
||||
1. `openid` 只作为后端微信身份绑定依据,不直接暴露给 H5 当登录凭证。
|
||||
2. H5 继续消费本系统 JWT,也就是 `#auth_provider=wechat&auth_token=...&auth_binding_status=...`。
|
||||
3. 这与 [`WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md`](./WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md) 中“微信只提供三方身份,Axum 签发系统 JWT”的边界一致。
|
||||
|
||||
## 2. 文件入口
|
||||
|
||||
@@ -20,6 +27,8 @@
|
||||
| `miniprogram/app.json` | 小程序全局配置,注册 `pages/web-view/index`。 |
|
||||
| `miniprogram/config.js` | 业务域名入口配置,需要部署时填写。 |
|
||||
| `miniprogram/pages/web-view/index.*` | 最小 web-view 页面。 |
|
||||
| `server-rs/crates/api-server/src/wechat_auth.rs` | 新增小程序登录接口 `/api/auth/wechat/miniprogram-login`。 |
|
||||
| `server-rs/crates/platform-auth/src/lib.rs` | 新增 `jscode2session` 兑换能力。 |
|
||||
|
||||
## 3. 需要手工填写的配置
|
||||
|
||||
@@ -27,14 +36,36 @@
|
||||
|
||||
```js
|
||||
const WEB_VIEW_ENTRY_URL = 'https://你的H5业务域名/';
|
||||
const API_BASE_URL = 'https://你的服务器域名/';
|
||||
const MINI_PROGRAM_APP_ID = '你的微信小程序 AppID';
|
||||
const MINI_PROGRAM_ENV = 'develop';
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
1. 必须是 `https`。
|
||||
2. 不能是 `localhost` 或 IP。
|
||||
3. 域名需要在微信小程序后台配置为业务域名。
|
||||
4. H5 页面里的 API、图片、音视频、iframe 等外链也要满足微信侧域名与证书要求。
|
||||
3. `WEB_VIEW_ENTRY_URL` 域名需要在微信小程序后台配置为业务域名。
|
||||
4. `API_BASE_URL` 域名需要在微信小程序后台配置为 request 合法域名。
|
||||
5. H5 页面里的 API、图片、音视频、iframe 等外链也要满足微信侧域名与证书要求。
|
||||
|
||||
在 `api-server` 环境变量中填写:
|
||||
|
||||
```bash
|
||||
WECHAT_AUTH_ENABLED=true
|
||||
WECHAT_AUTH_PROVIDER=real
|
||||
WECHAT_MINI_PROGRAM_APP_ID="你的微信小程序 AppID"
|
||||
WECHAT_MINI_PROGRAM_APP_SECRET="你的微信小程序 AppSecret"
|
||||
```
|
||||
|
||||
如果开放平台网页 OAuth 与小程序使用同一个 AppID/Secret,也可以继续使用已有:
|
||||
|
||||
```bash
|
||||
WECHAT_APP_ID="你的微信 AppID"
|
||||
WECHAT_APP_SECRET="你的微信 AppSecret"
|
||||
```
|
||||
|
||||
但正式部署建议把小程序配置写到 `WECHAT_MINI_PROGRAM_APP_ID` 与 `WECHAT_MINI_PROGRAM_APP_SECRET`,避免和网页 OAuth 配置混淆。
|
||||
|
||||
`WEB_VIEW_SOURCE_QUERY` 默认附加:
|
||||
|
||||
@@ -43,19 +74,61 @@ clientType=mini_program
|
||||
clientRuntime=wechat_mini_program
|
||||
```
|
||||
|
||||
后续如果 H5 或 `api-server` 需要更严格地区分来源,应优先使用请求头或登录态中的客户端身份字段,不要只信任 URL query。
|
||||
小程序壳调用登录接口时会补传:
|
||||
|
||||
## 4. 微信后台配置
|
||||
```text
|
||||
x-client-type=mini_program
|
||||
x-client-runtime=wechat_mini_program
|
||||
x-client-platform=ios|android|unknown
|
||||
x-client-instance-id=<小程序本地持久化随机值>
|
||||
x-mini-program-app-id=<MINI_PROGRAM_APP_ID>
|
||||
x-mini-program-env=<MINI_PROGRAM_ENV>
|
||||
```
|
||||
|
||||
这些字段会进入 refresh session 的客户端身份快照;URL query 只作为 H5 识别宿主来源的轻量标记,不作为鉴权依据。
|
||||
|
||||
## 4. 登录链路
|
||||
|
||||
当前登录链路固定为:
|
||||
|
||||
1. 小程序页面启动。
|
||||
2. 调用 `wx.login` 获取一次性 `code`。
|
||||
3. 小程序壳请求:
|
||||
|
||||
```http
|
||||
POST /api/auth/wechat/miniprogram-login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"code": "wx.login 返回的 code"
|
||||
}
|
||||
```
|
||||
|
||||
4. `api-server` 调用微信 `jscode2session` 兑换 `openid/unionid`。
|
||||
5. `api-server` 复用现有微信身份逻辑:
|
||||
- 先按 `unionid` 命中已有身份
|
||||
- 再按 `openid` 命中已有身份
|
||||
- 都没有命中时创建 `pending_bind_phone` 的微信壳账号
|
||||
6. `api-server` 签发系统 access token,并写入 refresh session。
|
||||
7. 小程序壳打开:
|
||||
|
||||
```text
|
||||
https://你的H5业务域名/#auth_provider=wechat&auth_token=<系统JWT>&auth_binding_status=active|pending_bind_phone
|
||||
```
|
||||
|
||||
8. H5 复用 `consumeAuthCallbackResult()` 消费 `auth_token` 并进入现有登录态恢复流程。
|
||||
|
||||
## 5. 微信后台配置
|
||||
|
||||
至少需要在小程序后台配置:
|
||||
|
||||
1. `业务域名`:承载 H5 的域名。
|
||||
2. `request 合法域名`:H5 里如果仍有小程序原生层直接请求 API,才需要配置;当前 web-view 壳本身不直接请求业务 API。
|
||||
2. `request 合法域名`:`API_BASE_URL` 对应的服务器域名。
|
||||
3. `socket 合法域名`:若后续小程序原生层直连 WebSocket 才需要;当前不启用。
|
||||
|
||||
当前仓库的 H5 仍建议通过同域 `/api/*` 访问 Rust `api-server`,避免在小程序和 H5 中分别维护跨域白名单。
|
||||
|
||||
## 5. 当前不做的事
|
||||
## 6. 当前不做的事
|
||||
|
||||
本次不做原生小程序页面迁移,原因是当前主前端依赖:
|
||||
|
||||
@@ -66,9 +139,14 @@ clientRuntime=wechat_mini_program
|
||||
|
||||
这些能力不能稳定原样运行在原生小程序宿主中。后续如要原生化,应新建小程序端宿主,复用 `packages/shared` 契约和 `api-server` BFF,而不是把 `src/` 整体搬过去。
|
||||
|
||||
## 6. 验收口径
|
||||
本次也不做 `openid` query 直登。原因是 `openid` 不是本系统签发的登录凭证,不能表达 token 版本、会话 ID、绑定状态、角色与过期时间,也不能被 H5 直接信任。
|
||||
|
||||
## 7. 验收口径
|
||||
|
||||
1. 微信开发者工具打开项目根目录后,识别 `miniprogram/` 为小程序源码目录。
|
||||
2. 未填写 `WEB_VIEW_ENTRY_URL` 时,页面显示配置提示,不出现空白页。
|
||||
3. 填写已配置业务域名后,首页全屏打开 H5。
|
||||
4. H5 内登录、SSE、图片资源等能力按 H5 自身部署域名和 `/api/*` 代理链路验证。
|
||||
2. 未填写 `WEB_VIEW_ENTRY_URL` 或 `API_BASE_URL` 时,页面显示配置提示,不出现空白页。
|
||||
3. 填写已配置业务域名后,小程序先请求 `/api/auth/wechat/miniprogram-login`。
|
||||
4. 后端返回 `token/bindingStatus/user`,并写入 refresh cookie。
|
||||
5. 首页全屏打开 H5,URL hash 中包含 `auth_provider=wechat`、`auth_token`、`auth_binding_status`。
|
||||
6. H5 内 `consumeAuthCallbackResult()` 消费 hash 后,`/api/auth/me` 能返回当前用户。
|
||||
7. `/api/auth/sessions` 能看到来源为 `mini_program / wechat_mini_program` 的会话记录。
|
||||
|
||||
Reference in New Issue
Block a user