7.2 KiB
7.2 KiB
密码登录与自动建号落地设计
日期:2026-04-21
1. 文档目的
这份文档用于指导 M2 中以下两条任务的首版落地:
实现密码登录实现账号自动创建 / 幂等登录兼容策略
目标是先把当前 Node 已经稳定运行的 /api/auth/entry 语义迁到 Rust 工作区,并冻结:
api-server对外暴露的最小兼容接口。module-auth负责的密码登录用例边界。- 自动建号与并发幂等兼容规则。
- 登录成功后与 JWT、refresh cookie 的衔接方式。
2. 当前基线
当前 Node /api/auth/entry 主链已经具备如下语义:
- 输入
username + password。 - 若用户名不存在,则自动创建一个本地账号。
- 若用户名已存在,则校验密码。
- 登录成功后签发 access token。
- 同时创建 refresh session,并把原始 refresh token 写入 HttpOnly cookie。
- 并发创建同一用户名时,后到的请求会回退为“查已存在账号并校验密码”,不因唯一键冲突直接失败。
这条链路既是当前前端匿名/游客恢复的基础,也是真实 /api/auth/entry contract 的既有事实,因此 Rust 首版必须兼容。
3. 设计输入
本任务直接受以下文档约束:
- SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md
- SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md
- OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md
- PLATFORM_AUTH_JWT_ADAPTER_DESIGN_2026-04-21.md
- PLATFORM_AUTH_REFRESH_COOKIE_ADAPTER_DESIGN_2026-04-21.md
关键冻结点:
password_hash当前继续由user_account承担,不进入auth_identity。sub必须是稳定user_id。- 登录成功后必须继续同时生成 access token 和 refresh session。
- 自动建号兼容必须保留,不能因为迁到 Rust 就删除。
4. 首版落地范围
本阶段只落以下内容:
module-auth中的密码登录用例。api-server中的POST /api/auth/entry。- 用户名校验、密码哈希校验与自动建号。
- 登录成功后的 access token 与 refresh cookie 主链打通。
本阶段明确不包含:
- SpacetimeDB 真正的
user_account/refresh_sessionreducer 写入。 /api/auth/me、/api/auth/logout、/api/auth/refresh的正式业务闭环。- 手机验证码与微信登录链路。
5. crate 边界
5.1 module-auth
负责:
- 用户名与密码的领域校验。
- 密码登录主用例。
- 自动建号与并发幂等兼容策略。
- 输出登录成功所需的最小用户快照。
不负责:
- JWT 编解码。
- refresh cookie 解析与写回。
- HTTP 请求解析与响应拼装。
5.2 platform-auth
负责:
- 密码哈希与校验适配。
- JWT 签发与校验。
- refresh cookie 读写适配。
不负责:
- 决定账号是否应当自动创建。
- 决定用户状态是否合法。
5.3 api-server
负责:
- 解析
POST /api/auth/entry请求体。 - 调用
module-auth用例。 - 调用
platform-auth签发 token 和 refresh cookie。 - 返回与旧接口兼容的 JSON body。
6. 请求与响应 contract
6.1 请求体
固定沿用当前 contract:
{
"username": "guest_001",
"password": "secret123"
}
6.2 成功响应
固定沿用当前 contract:
{
"token": "<access-token>",
"user": {
"id": "user_xxx",
"username": "guest_001",
"displayName": "guest_001",
"phoneNumberMasked": null,
"loginMethod": "password",
"bindingStatus": "active",
"wechatBound": false
}
}
同时响应头必须写回 refresh cookie。
7. 用户名与密码规则
当前阶段继续对齐 Node 基线:
username只允许3到24位字母、数字、下划线。password长度必须在6到128位之间。
任一校验失败时:
- 返回
400 BAD_REQUEST - 错误文案继续保持中文
8. 自动建号与幂等兼容
8.1 自动建号
当 username 不存在时:
- 用当前请求里的
password生成密码哈希。 - 创建一条本地账号。
display_name = usernamelogin_provider = passwordaccount_status = activetoken_version = 1
8.2 已存在账号
当 username 已存在时:
- 校验密码哈希。
- 校验失败返回
401 UNAUTHORIZED。 - 校验成功继续登录。
8.3 并发幂等兼容
若两个请求并发创建同一用户名:
- 允许其中一个请求先创建成功。
- 后一个请求若命中唯一键冲突,不直接失败。
- 后一个请求必须重新查询该用户名。
- 若查到账号,则按“已存在账号”路径继续校验密码。
这保证了当前前端重复调用 /api/auth/entry 时可以恢复同一账号,而不是随机失败。
9. 首版存储策略
当前阶段为了先跑通工程闭环,固定采用:
module-auth内的进程内内存仓储适配器作为临时真相。
说明:
- 这是阶段性工程策略,不改变最终
SpacetimeDB作为真相源的目标。 - 当前这样做是为了先把 crate 边界、用例形状、HTTP contract、JWT / refresh cookie 主链稳定下来。
- 后续切到
SpacetimeDB时,应保持module-auth用例接口不变,只替换仓储实现。
10. 密码哈希策略
当前阶段继续对齐 Node:
Argon2id
说明:
- Rust 侧不再复用 Node 原生库,但哈希语义继续保持同类算法。
- 当前目标是“工程能力闭环”,不是做跨语言哈希值兼容迁移。
- 若未来需要与 Node 历史哈希共存,需单独补兼容文档和迁移策略。
11. 与 JWT / refresh cookie 的衔接
密码登录成功后:
module-auth返回最小用户领域对象。api-server基于该对象构造AccessTokenClaimsInput。platform-auth签发 access token。platform-auth生成 refresh token 与Set-Cookie头。api-server返回token + user。
当前阶段固定 claims 值:
provider = passwordroles = ["user"]binding_status = activephone_verified = falsedisplay_name = username
12. 测试策略
当前阶段至少覆盖:
- 首次密码登录自动建号成功。
- 同用户名同密码可重复登录同一账号。
- 同用户名不同密码返回
401。 - 非法用户名返回
400。 - 登录成功时返回 access token。
- 登录成功时写回 refresh cookie。
13. 完成定义
满足以下条件时,本任务视为完成:
module-auth不再只是 README,占位被真实 crate 实现替换。POST /api/auth/entry可在 Rust 侧独立跑通。- 自动建号与幂等兼容行为可验证。
- JWT 与 refresh cookie 登录成功主链打通。
- 文档、任务清单与测试同步完成。
14. 后续衔接
这条任务完成后,下一步顺序固定为:
me查询- refresh token 轮换
- 会话吊销
- 手机验证码登录
微信登录继续按“暂缓执行”处理,直到用户重新解锁。