Files
story/docs/customer-sms-login.md
T

71 lines
5.5 KiB
Markdown
Raw Normal View History

# 家庭端手机号验证码登录
家庭平台使用独立 `/login` 页面,默认“手机号 + 短信验证码”,无需先打开设备绑定链接。未登录访问业务页会自动跳转,登录后返回原目标页面。当前页面默认中国大陆 +86 手机号;后台继续接受 E.164 格式。维护端保留 OIDC 登录。
## 已实现
- 手机号校验、6 位验证码、60 秒重发倒计时、5 分钟过期提示;改手机号后清除旧挑战和验证码。
- 独立匿名登录会话,无设备、绑定码、RTC 服务也能登录。
- 服务端验证码只存 keyed hash,手机号加密存储;验证码限 5 次尝试、一次性消费,并绑定发送时的浏览器。
- 手机号限额与设备绑定短信共用:60 秒内不得重发、每小时最多 5 次;另有 IP、浏览器会话及全局限额。
- 相同请求号重试不重复发送;网关结果不确定时明确提示,不伪造成功、不返回测试验证码。
- 登录后使用 HttpOnly、Secure、SameSite=Lax 账号 Cookie,24 小时有效。重新加载页面恢复登录;家庭写请求携带 CSRF,仍执行原有家庭/记忆/敏感权限校验。
- 同一账号 Cookie 的 CSRF 可跨标签页重新读取;过期、撤销和退出后业务接口返回 401,页面移除原有私密内容。
- 账号 Cookie 只开放明确列出的家庭接口;维护接口、设备协议和服务商回调保持各自认证方式。
入口:`GET /api/v1/web/login/session`、`POST /api/v1/web/login/sms/send`、`POST /api/v1/web/login/sms/verify`。登录后继续通过 `GET /api/v1/me` 读取服务端账号和家庭权限。
## 真实短信配置
当前代码支持原有 HTTPS 短信网关,也支持直接调用腾讯云短信服务;部署配置和小程序接口见[腾讯云验证码短信接入](tencent-sms-integration.md)。未配置真实短信供应商时不会发送短信。浏览器自动化中使用的验证码只存在于测试脚本,不是产品可用的固定验证码。
API 和 worker 需要相同的部署配置:
| 配置 | 用途 |
|---|---|
| `ONBOARDING_SMS_LOGIN_ENABLED=true` | 开启设备绑定及家庭首页短信入口 |
| `ONBOARDING_MASTER_KEY` 或对应 `_FILE` | 已有 32 字节主密钥的十六进制值;已有环境继续使用原密钥 |
| `ONBOARDING_ENTRY_URL` | 与家庭网站同域的固定 HTTPS 入口,例如 `https://example.com/onboarding/entry`,用于同源检查 |
| `ONBOARDING_TRUSTED_PROXY_CIDRS` | 明确的反向代理网络;代理转发 `X-Forwarded-Proto: https` |
| `ONBOARDING_SMS_GATEWAY_URL` | 固定 HTTPS 网关 POST 地址,不带账号、查询串或跳转 |
| `ONBOARDING_SMS_GATEWAY_TOKEN` 或对应 `_FILE` | 网关访问凭据 |
| `ONBOARDING_SELF_SIGNUP_TENANT_IDS` | 配置唯一的租户 public_id 时,未注册手机号可创建该租户下的普通客户账号;空或多个值时仅登录已有短信账号,新用户通过设备绑定入口按设备所属租户注册 |
首页短信登录不依赖 `ONBOARDING_ENABLED`、设备开通或 RTC 开关。部署时运行迁移 `000060_customer_sms_login`,并使用配套 API、worker、Web。无需刷机。完整开发环境执行 `scripts/dev.ps1` 会同时应用其他尚未执行的迁移,包含此前的通话记忆改动,应在联调环境使用。
网关约定:请求 Header 为 `Authorization: Bearer <gateway-token>` 和 `Idempotency-Key: <challenge-id>`,JSON 为:
```json
{
"phone_e164": "+8613800000000",
"code": "本次随机生成的六位数字",
"purpose": "LOGIN",
"request_id": "挑战UUID",
"expires_in_seconds": 300
}
```
网关必须按 request_id 去重;接受发送返回 2xx。当前接口是统一网关协议,不可直接把阿里云/腾讯云等供应商的原生 API 地址填进来;需由网关完成签名、短信签名名称及模板映射,或另接对应供应商适配器。
## 旧账号
已有设备绑定短信账号直接复用原身份和家庭数据。原 `owner` 等 OIDC 账号没有已验证手机号关联,不能凭输入某个手机号自动合并。因此独立登录页保留“账号登录”;手机新账号不会自动继承原 OIDC 账号的家庭。若要完全关闭原账号入口,需要先完成已验证手机号与旧账号的迁移。
## 验证
在仓库根目录运行(先启动本地 PostgreSQL 和 Redis):
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& './scripts/test-onboarding-integration.ps1' -Packages onboarding,httpapi -VerboseTests -OutputDirectory '.tmp-sms-login'"
go test ./...
go vet ./...
npm run build --prefix web
$env:PLAYWRIGHT_REQUIRE_FROM = "$env:USERPROFILE/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/package.json"
node test/e2e/customer-sms-login.mjs
node test/e2e/login-routing.mjs
```
数据库脚本创建临时库并自动清理,不迁移现有业务库。新增测试覆盖无设备登录、Cookie/CSRF/退出、重复发送、设备入口共享限额、错误次数/过期、跨浏览器/跨站、网关失败、已有账号复用和禁止未授权注册。短信发送器为内存测试替身,数据库真实。
浏览器测试运行真实登录页面、路由、认证状态和 API 客户端,验证短信交互与登录返回流程。证据在 `.design-evidence/customer-sms-login/` 和 `.design-evidence/login-routing/`,包含桌面/手机截图和 `result.json`。设备绑定入口及 OIDC 返回规则见 [Web 登录与页面恢复](web-authentication-session.md)。真实短信送达、供应商签名模板和生产 HTTPS 反向代理仍需完成配置后实测。