# Axum 手机验证码登录最小闭环设计 日期:`2026-04-21` ## 1. 文档目的 这份文档用于冻结 Rust `api-server + module-auth` 当前阶段手机号验证码登录的最小实现边界,避免在接入真实阿里云短信 adapter 前,把接口 contract、mock 行为和后续 SpacetimeDB 数据模型混在一起。 ## 2. 当前阶段范围 本阶段只落以下内容: 1. `POST /api/auth/phone/send-code` 2. `POST /api/auth/phone/login` 3. 进程内 mock 验证码存储与校验 4. 登录成功后签发 access token 与 refresh cookie 本阶段明确不包含: 1. 真实阿里云短信发送 2. 图形验证码 3. 风控封禁 4. 手机号换绑 5. 微信补绑手机号 ## 3. 固定 mock 规则 当前阶段统一使用以下 mock 约束: 1. 固定验证码:`123456` 2. 验证码 TTL:`5` 分钟 3. 冷却秒数:`60` 4. 手机号必须按中国大陆手机号规则校验 ## 4. 接口 contract ### 4.1 `POST /api/auth/phone/send-code` 请求体: ```json { "phone": "13800138000", "scene": "login" } ``` 当前允许的 `scene`: 1. `login` 2. `bind_phone` 3. `change_phone` 成功响应: ```json { "ok": true, "cooldownSeconds": 60, "expiresInSeconds": 300, "providerRequestId": null } ``` ### 4.2 `POST /api/auth/phone/login` 请求体: ```json { "phone": "13800138000", "code": "123456" } ``` 成功响应: ```json { "token": "access-token", "user": { "id": "user_00000001", "username": "phone_00000002", "displayName": "138****8000", "phoneNumberMasked": "138****8000", "loginMethod": "phone", "bindingStatus": "active", "wechatBound": false } } ``` 同时必须写回 refresh cookie。 ## 5. 用户创建与复用规则 1. 手机号首次登录时自动建号 2. 同一手机号再次登录时复用同一系统用户 3. access token 的 `provider` 固定为 `phone` 4. 用户快照中的 `loginMethod` 固定为 `phone` ## 6. 错误语义 当前阶段统一约束为: 1. 手机号格式错误:`400` 2. 场景非法:`400` 3. 验证码不存在、错误或过期:`400` 4. 服务内部存储或签发失败:`500` ## 7. crate 边界 ### 7.1 `module-auth` 负责: 1. 手机号规范化 2. mock 验证码写入与校验 3. 手机号用户创建与复用 ### 7.2 `api-server` 负责: 1. HTTP 请求解析 2. 场景字符串映射 3. 登录成功后创建会话、签发 JWT、写回 refresh cookie ### 7.3 `platform-auth` 负责: 1. 签发 access token 2. 生成 refresh cookie ## 8. 测试要求 至少覆盖: 1. `send-code` 成功返回 mock 冷却与过期秒数 2. 默认错误场景返回 `400` 3. 首次手机号登录会自动建号并写 refresh cookie 4. 同一手机号重复登录复用同一用户 ## 9. 完成定义 满足以下条件时,本任务视为完成: 1. Rust 侧已提供 `send-code` 与 `phone/login` 2. mock 验证码链路可跑通 3. 手机号登录成功后能拿到 access token 与 refresh cookie 4. 文档、任务清单与测试已同步更新