Files
story/docs/customer-sms-login.md
T
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

71 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 家庭端手机号验证码登录
家庭平台使用独立 `/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 反向代理仍需完成配置后实测。