# 密码登录、修改与重置落地设计 日期:`2026-04-24` ## 1. 目标边界 本次迭代开放手机号密码登录、登录后修改密码、手机号验证码重置密码,但不开放独立注册页面。 1. 新用户只能通过手机号验证码完成注册与首次登录。 2. 已有用户可以在登录后设置或修改密码。 3. 忘记密码时,只能通过已绑定手机号验证码重置密码。 4. 密码登录只校验已存在且已设置密码的手机号账号,不自动创建新账号。 5. 登录面板本地缓存最近一次成功登录的手机号,只用于回填手机号输入框,不缓存密码或验证码。 ## 2. 接口设计 ### 2.1 密码登录 沿用现有 `POST /api/auth/entry`: 1. 请求字段固定为 `phone`、`password`,前端只提交手机号。 2. 后端只按标准手机号归一化后查找账号,不兼容邮箱、用户名、百梦号或历史开发游客标识。 3. 手机号不存在时返回 `401`,不创建账号。 4. 手机号存在但未设置密码时返回 `401`。 5. 校验成功后签发 access token,并写入 refresh cookie。 ### 2.2 修改密码 新增 `POST /api/auth/password/change`: 1. 需要 Bearer 登录态。 2. 请求字段:`currentPassword`、`newPassword`。 3. 若账号未设置过密码,允许 `currentPassword` 为空并设置首个密码。 4. 若账号已有密码,必须校验 `currentPassword` 后才能写入 `newPassword`。 5. 修改成功后递增用户 `token_version`,使旧 access token 失效;前端沿用当前 refresh 会话刷新登录态。 ### 2.3 重置密码 新增 `POST /api/auth/password/reset`: 1. 不需要 Bearer 登录态。 2. 请求字段:`phone`、`code`、`newPassword`。 3. 使用 `reset_password` 短信场景校验验证码。 4. 手机号不存在时返回 `401`,避免用密码重置隐式注册账号,并避免泄露手机号注册状态。 5. 重置成功后签发新的 access token,并写入 refresh cookie,便于用户直接进入登录态。 ### 2.4 发送重置验证码 复用 `POST /api/auth/phone/send-code`,`scene` 增加 `reset_password`。 ### 2.5 验证码注册/登录 复用现有 `POST /api/auth/phone/login`: 1. 请求字段:`phone`、`code`。 2. 验证码校验成功后,若手机号已绑定账号,则直接完成登录。 3. 验证码校验成功后,若手机号没有账号信息,则后端自动创建手机号账号,再完成登录。 4. 自动创建账号默认不设置用户可用密码,用户后续可在账号设置或忘记密码流程设置密码。 ## 3. 前端交互 登录弹窗不再拆独立注册页签: 1. 面板直接展示手机号和密码输入,用于已设置密码账号登录。 2. 密码登录按钮文本固定为 `登录`,不允许暗示密码入口具备注册能力。 3. 忘记密码入口显示在登录按钮右下侧,点击后仍进入独立重置面板,不在当前表单下方展开。 4. 同一面板保留手机号验证码注册/登录能力,用于新用户自动注册和已注册用户免密码登录。 5. 账号设置面板提供密码修改入口;未设置密码的账号显示为设置密码。 ## 4. 数据约束 进程内认证快照中的 `password_hash` 改为可空语义: 1. 手机号新建账号默认没有用户可用密码。 2. 微信待绑定账号默认没有用户可用密码。 3. 只有用户显式修改或重置密码后,才允许密码登录。 后续迁移到 SpacetimeDB 表时,保持同一语义:密码哈希字段允许为空,密码登录 reducer 不承担注册能力,验证码登录 reducer 承担“无账号则自动注册”的唯一注册入口。 ## 5. 2026-05-12 快照同步修复 重置密码和修改密码都会改变认证真相:`password_hash`、`password_login_enabled`、`token_version`,重置密码还会立即创建新的 refresh session。因此 API 层在 `POST /api/auth/password/change` 与 `POST /api/auth/password/reset` 成功后,必须和密码登录、手机号登录、刷新、退出一样调用 `sync_auth_store_snapshot_to_spacetime()`。 若只更新本地 `InMemoryAuthStore` 而不同步 SpacetimeDB 认证快照,`api-server` 重启时可能从旧的 SpacetimeDB 表或旧快照恢复账号状态,表现为用户已通过忘记密码重设成功,但再次密码登录仍返回“手机号或密码错误”。启动恢复时应从 SpacetimeDB 表、SpacetimeDB 快照记录和本地 `GENARRATIVE_AUTH_STORE_PATH` 文件中选择可判断的最新快照;当本地文件更新且远端表无更新时间戳时,优先使用本地文件并尝试回写 SpacetimeDB,避免旧远端状态覆盖刚重设的密码。 验证命令: ```bash cargo test -p module-auth password --manifest-path server-rs/Cargo.toml cargo test -p api-server password --manifest-path server-rs/Cargo.toml ```