feat: add platform auth jwt adapter

This commit is contained in:
2026-04-21 13:02:44 +08:00
parent e37163d4d3
commit adaf514a1a
20 changed files with 1220 additions and 44 deletions

View File

@@ -0,0 +1,217 @@
# platform-auth JWT 适配设计
日期:`2026-04-21`
## 1. 文档目的
这份文档用于指导 `platform-auth` 首个真实能力落地,目标是完成:
1. `platform-auth` crate 的 JWT claims 结构。
2. access token 的签发与校验适配。
3. `api-server` 的最小 Bearer JWT 校验入口。
这一步只解决“Axum 自身能稳定签发并校验 JWT”的基础问题不提前把 refresh cookie、短信和微信 OAuth 一起耦合进来。
## 2. 当前落地范围
本阶段只包含以下实现:
1. `JwtConfig`
2. `AccessTokenClaimsInput`
3. `AccessTokenClaims`
4. `sign_access_token(...)`
5. `verify_access_token(...)`
6. `api-server` 的 Bearer 鉴权中间件
7. `/_internal/auth/claims` 内部调试路由
本阶段明确不包含:
1. refresh cookie 的生成、解析、轮换和吊销。
2.`module-auth` / `SpacetimeDB` 真相表读取 `token_version` 并做在线比对。
3. 短信 provider 与微信 OAuth 平台适配。
4. SpacetimeDB 模块对 Axum 签发 JWT 的消费代码。
## 3. 设计输入
本实现直接受以下文档约束:
1. [OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md](./OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md)
2. [SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](./SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md)
关键冻结点:
1. `iss/sub/sid/provider/roles/ver/phone_verified/binding_status` 是当前 access token 的固定字段。
2. `sub` 是稳定 `user_id`
3. `sid` 是会话 ID不是单次 token ID。
4. `roles` 当前至少包含 `user`
5. 不允许把手机号、openid、风控状态、refresh token hash 放进 JWT。
## 4. crate 边界
### 4.1 `platform-auth`
负责:
1. 组织 JWT 结构。
2. 执行签名与验签。
3. 执行基础 claims 完整性校验。
不负责:
1. 用户是否存在。
2. `token_version` 是否仍是数据库最新值。
3. refresh session 是否已被吊销。
4. provider 之外的业务规则判断。
### 4.2 `api-server`
负责:
1.`Authorization` 头提取 Bearer token。
2. 调用 `platform-auth` 校验。
3. 把已校验 claims 写入请求上下文。
4. 在本阶段提供最小内部验收入口 `/_internal/auth/claims`
不负责:
1. 自己再实现一套 JWT 编解码逻辑。
2. 把 claims 结构拆散成多个重复 helper。
## 5. 配置口径
当前阶段 `api-server` 读取并传入 `platform-auth` 的配置如下:
| 配置项 | 环境变量 | 默认值 | 说明 |
| --- | --- | --- | --- |
| issuer | `GENARRATIVE_JWT_ISSUER` | `https://auth.genarrative.local` | OIDC 风格发行者标识。 |
| secret | `GENARRATIVE_JWT_SECRET` | `genarrative-dev-secret` | 当前阶段沿用对称签名密钥。 |
| access token TTL | `GENARRATIVE_JWT_ACCESS_TOKEN_TTL_SECONDS` | `7200` | access token 有效期,单位秒。 |
兼容回退:
1. `issuer` 可回退读取 `JWT_ISSUER`
2. `secret` 可回退读取 `JWT_SECRET`
3. TTL 可回退读取 `JWT_EXPIRES_IN`,支持:
- 纯秒值,例如 `900`
- `s/m/h/d` 后缀,例如 `30m``2h`
## 6. 算法选择
当前阶段固定采用:
1. `alg = HS256`
理由:
1. 与当前 Node 基线兼容,迁移阻力最低。
2. 先把 claims、配置口径和 Bearer 主链稳定下来。
3. 若未来升级到非对称签名,应作为独立任务处理,而不是夹带进当前重写链路。
## 7. Rust 结构设计
### 7.1 `AccessTokenClaimsInput`
用途:
1. 作为业务层输入。
2. 不承载 `iat/exp/iss` 这种平台计算字段。
字段:
1. `user_id`
2. `session_id`
3. `provider`
4. `roles`
5. `token_version`
6. `phone_verified`
7. `binding_status`
8. `display_name`
### 7.2 `AccessTokenClaims`
用途:
1. 对应最终 JWT payload。
2. 直接用于签名与验签结果输出。
字段:
1. `iss`
2. `sub`
3. `sid`
4. `provider`
5. `roles`
6. `ver`
7. `phone_verified`
8. `binding_status`
9. `display_name`
10. `iat`
11. `exp`
## 8. 校验规则
### 8.1 签发前校验
1. `issuer``secret` 不能为空。
2. TTL 必须大于 `0`
3. `sub` 不能为空。
4. `sid` 不能为空。
5. `roles` 不能为空数组。
6. `exp` 必须晚于 `iat`
### 8.2 验签时校验
1. 算法必须是 `HS256`
2. 签名必须正确。
3. `iss` 必须匹配当前配置。
4. `exp/iat/iss/sub` 必须存在。
5. 反序列化后的 `sid/provider/roles/ver/phone_verified/binding_status` 必须完整。
当前阶段不做的校验:
1. `ver` 与数据库最新 token version 比对。
2. `sid``refresh_session` 活跃状态比对。
3. `roles` 的细粒度授权判断。
## 9. api-server 最小接线
本阶段 `api-server` 接线规则如下:
1. `AppConfig` 增加 JWT 相关配置。
2. `AppState` 在启动时构造唯一一份 `JwtConfig`
3. `require_bearer_auth` 中间件从请求头读取 Bearer token。
4. 验签成功后把 claims 以 `AuthenticatedAccessToken` 写入 request extensions。
5. 内部路由 `/_internal/auth/claims` 用于返回当前已校验 claims作为阶段验收与调试入口。
说明:
1. 这个内部路由不是最终对外 contract。
2. 它的存在是为了在 `module-auth` 与正式 `/api/auth/me` 落地前,先把 Bearer 主链单独跑通。
## 10. 测试策略
当前阶段要求至少覆盖:
1. `platform-auth` 的 JWT 签发与验签回环。
2. issuer 不匹配时拒绝。
3. 空角色拒绝。
4. `api-server` 在无 Bearer token 时返回 `401`
5. `api-server` 在合法 Bearer token 下返回 claims。
## 11. 完成定义
当以下条件满足时,本任务视为完成:
1. Rust workspace 中存在真实可编译的 `platform-auth` crate。
2. `api-server` 已能使用 `platform-auth` 校验 Bearer JWT。
3. 工作区测试与编译可通过。
4. 任务清单已同步更新。
## 12. 后续衔接
下一阶段继续衔接:
1. refresh cookie 读取与轮换。
2. `module-auth` 会话真相与 `token_version` 在线校验。
3. `/api/auth/me``/api/auth/refresh` 等正式接口。
4. SpacetimeDB 对 Axum JWT 的身份透传验证。

View File

@@ -4,6 +4,7 @@
## 文档列表
- [PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md](./PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md)`platform-auth` 首版 JWT 适配设计,冻结 `JwtConfig`、claims 结构、`HS256` 签发/校验、`api-server` Bearer 中间件与内部验收路由边界。
- [OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md](./OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md):面向 Axum、`platform-auth``SpacetimeDB` 身份透传的 OIDC 风格 JWT claims 设计,冻结 `iss/sub/sid/provider/roles` 等关键字段。
- [RUST_SHARED_LOGGING_CRATE_DESIGN_2026-04-21.md](./RUST_SHARED_LOGGING_CRATE_DESIGN_2026-04-21.md)Rust 工作区统一日志模块 `shared-logging` 的职责边界、API、输出风格与 `api-server` 迁移规则。
- [SPACETIMEDB_WECHAT_AUTH_STATE_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_WECHAT_AUTH_STATE_TABLE_DESIGN_2026-04-21.md)`M2` 第七张微信 OAuth 状态表 `wechat_auth_state` 的字段、过期/消费语义、`wechat/start``wechat/callback` 的单次消费规则,以及多实例下的清理策略。