Files
Genarrative/docs/technical/AUTH_LOGIN_OPTIONS_DESIGN_2026-04-21.md
五香丸子 46a254f142
Some checks failed
CI / verify (push) Has been cancelled
feat: add child motion entry and fix auth env
2026-05-10 18:27:51 +08:00

6.9 KiB
Raw Blame History

/api/auth/login-options 登录方式选项设计

日期:2026-04-21

1. 文档目的

这份文档用于冻结 Rust api-server 首版 GET /api/auth/login-options 的返回 contract、配置来源与当前阶段边界确保前端在登录页读取“当前可用登录方式”时不需要依赖硬编码开关。

2. 当前目标

当前阶段只解决一件事:

  1. Axum 根据服务端配置,返回当前环境启用的登录方式列表。
  2. 密码登录入口由 Rust password_entry 固定承载,作为登录弹窗的保底入口。

本阶段明确不包含:

  1. 短信或微信登录链路本身是否已经完整落地
  2. 对前端返回更细粒度的 provider 配置
  3. 第三方登录按钮文案、图标或 UI 布局规则

3. 接口 contract

3.1 请求

  1. 方法:GET
  2. 路径:/api/auth/login-options
  3. 鉴权:不需要
  4. 请求体:空

3.2 成功响应

{
  "availableLoginMethods": ["phone", "password", "wechat"]
}

字段说明:

  1. availableLoginMethods 为字符串数组
  2. 当前阶段只允许出现:
    • phone
    • password
    • wechat

3.3 返回顺序

返回顺序固定为:

  1. phone
  2. password
  3. wechat

这样可以保证前端按钮顺序稳定,不因配置解析顺序变化而漂移。

4. 配置来源

api-server 只读取以下布尔配置:

  1. SMS_AUTH_ENABLED
  2. WECHAT_AUTH_ENABLED

映射规则固定为:

  1. SMS_AUTH_ENABLED=true 时返回 phone
  2. Rust 密码登录主链可用时固定返回 password
  3. WECHAT_AUTH_ENABLED=true 时返回 wechat
  4. 短信与微信都关闭时仍返回 ["password"]

5. crate 边界

5.1 api-server

负责:

  1. 读取 AppState.config
  2. 组装 availableLoginMethods
  3. 返回项目兼容的响应 envelope

5.2 module-auth

本接口当前阶段不依赖 module-auth

5.3 前端

负责:

  1. 根据 availableLoginMethods 决定是否展示手机号 / 微信入口
  2. 不再假设某种登录方式一定存在
  3. /api/auth/login-options 联调失败或返回空数组,前端仍保留 password 入口,避免登录弹窗显示“当前登录入口暂不可用”后无法继续操作。

6. 测试要求

至少覆盖:

  1. 默认配置下返回 ["password"]
  2. 同时启用短信、密码与微信时返回 ["phone", "password", "wechat"]
  3. 前端在 login-options 读取失败或返回空数组时,仍展示密码登录表单

7. 完成定义

满足以下条件时,本任务视为完成:

  1. Rust 已提供 GET /api/auth/login-options
  2. 响应字段命名与前端约定一致
  3. 配置开关可稳定映射到返回数组
  4. 文档、任务清单与测试已同步更新

8. 2026-05-01 前端降级修复记录

本地联调时若 api-server 未启动或 Vite 代理暂时返回 500GET /api/auth/login-options 会失败。前端必须继续遵循第 5.3 节约束:

  1. AuthGatelogin-options 读取失败时设置 availableLoginMethods = ["password"]
  2. 该失败只代表登录方式配置探测失败,不代表登录功能不可用,因此不把 读取登录方式失败 写入登录弹窗错误条。
  3. 登录弹窗仍展示密码登录表单,玩家可继续登录后进入创作链路。
  4. 本地仍需要启动 api-server,否则后续 POST /api/auth/entry 等真实登录请求无法完成。

9. 2026-05-07 本地短信入口恢复记录

如果登录弹窗里短信登录页签“像是被删了”,先不要改前端表单,优先检查本地登录方式探测结果:

  1. 仓库根目录 .env.local 里必须显式保留 SMS_AUTH_ENABLED=true
  2. 本地启动请优先使用 npm run api-servernpm run dev:rustnpm run dev这些脚本会按“shell 环境优先、.env.local 覆盖 .env”合并配置。
  3. GET /api/auth/login-options 只返回 ["password"],说明短信入口没有被服务端配置打开,前端只是按 contract 正常降级。
  4. SMS_AUTH_ENABLED=true 生效时,GET /api/auth/login-options 至少应返回 ["phone", "password"],短信登录页签才会重新出现。

10. 2026-05-07 前端代理端口错配修复记录

如果 Rust API 直连返回 ["phone", "password"],但从前端域名请求 GET /api/auth/login-options 返回 500,短信页签同样会消失。此时不是登录 UI 被删除,而是 AuthGate 按第 5.3 节降级成 ["password"]

本地排查顺序固定为:

  1. 先请求 http://127.0.0.1:3000/api/auth/login-options,确认前端代理是否成功返回 JSON。
  2. 再请求当前 Rust API 目标,例如 http://127.0.0.1:3100/api/auth/login-optionshttp://127.0.0.1:8082/api/auth/login-options
  3. 若直连 API 成功而 3000 返回 500,检查 RUST_SERVER_TARGETGENARRATIVE_API_TARGETGENARRATIVE_RUNTIME_SERVER_TARGET 是否指向仍在监听的 API 端口。
  4. npm run dev / npm run dev:rust 完整栈默认由脚本计算 API 端口;加载 .env.local 给后端使用后,脚本必须重新固定 RUST_SERVER_TARGET,避免 .env.local 中的旧代理目标覆盖本次启动的实际 API 端口。
  5. npm run dev:web 只启动前端,不会自动拉起 Rust API如果 .env.local / 当前环境已经显式声明 GENARRATIVE_RUNTIME_SERVER_TARGETRUST_SERVER_TARGETGENARRATIVE_API_TARGETGENARRATIVE_API_PORT,脚本必须固定使用该目标。目标当下不可用时只打印警告,不自动切到另一个端口,避免前端进程长时间绑定到随后会停掉的临时 API。
  6. 如果 3000 仍然返回 500,先确认浏览器是不是还开着旧的前端进程。当前脚本如果因为端口占用漂移到 3001 / 3002,应直接关掉旧进程后重启,而不是继续用旧的 3000 页面判断登录入口状态。

11. 2026-05-10 npm run api-server 环境加载与短信 provider 排查记录

本地单独启动 api-server 时,环境变量合并顺序固定为:

外层 shell > .env > .env.local > .env.secrets.local

这保证 .env.local 能覆盖 .env.example 派生出的默认值,.env.secrets.local 能继续覆盖本地私密密钥配置。scripts/api-server-dev.mjs 不得让 .env 后加载并覆盖 .env.local,否则 SMS_AUTH_ENABLEDSMS_AUTH_PROVIDER 可能被压回错误值。

排查“点击获取验证码但手机收不到短信”时,除了确认 availableLoginMethods 包含 phone,还必须确认当前进程实际使用的 provider

  1. SMS_AUTH_PROVIDER="mock" 只用于本地 UI / 账号链路联调,不会向手机发送真实短信;此时应使用 SMS_AUTH_MOCK_VERIFY_CODE,默认 123456
  2. 真实短信链路必须使用 SMS_AUTH_PROVIDER="aliyun",并在修改 .env.local 后重启 api-server,运行中的进程不会自动切换 provider。
  3. 真实 provider 是否被使用,以 api-server 日志中的 provider=aliyunprovider_request_idprovider_out_id 为准。