# 新通短信登录

本参考用于负责人明确要求在新项目中接入与 Study Buddy 相同的新通手机号验证码登录时。它复用的是一个独立的服务端身份适配器、本站账号和会话模型；不是把另一个项目的数据库、浏览器 Cookie、上游 token、用户资料、权限或凭据迁移过来。

## 启用条件与配置

先确认新项目确实获准使用该新通身份服务，并取得该项目／环境独立的服务端配置。不得从 Study Buddy 的环境文件、日志或部署主机复制值。

服务端按环境提供下列配置，并在启动时校验完整性：

```text
XINTONG_BASE_URL
XINTONG_API_PREFIX
XINTONG_ROOT_API_PREFIX
XINTONG_WEB_URL
XINTONG_DEVICE_ID
XINTONG_SCHOOL_ID
```

它们全部只在服务端读取，不进入 Vite 或其他浏览器构建变量、镜像层、客户端错误或日志。任一配置缺失时，本地身份适配器标记为“未配置”；界面展示账号登录暂不可用，不能渲染看似可以提交的登录表单或转而调用上游。生产与测试使用各自获准的上游环境和本站独立的数据库、存储和 Cookie 上下文。

## 适配器边界

新增一个本地 `IdentityProvider`（名称可按项目调整），对外只提供：密码认证、短信发送、短信认证、首次设密和稳定身份资料。浏览器只能调用本站的 `/api/identity/*`，不直接请求新通接口，也不得得到上游 bearer token。

当前新通协议形态如下；路径、请求头、注册行为和参数以新项目获准的服务合同为准，集中在适配器内部，不能散落在路由或 React 组件中：

1. 服务端调用短信验证码发送接口（当前协议为 `POST /sms/captcha`）。发送前先在本站数据库预占发送额度；上游失败不自动重发。
2. 服务端用手机号和验证码调用验证码登录（当前为 `POST /user/codeLogin`），取得短期上游 token。
3. 使用该 token 读取用户资料（当前为 root 路径的 `/ieltsbuddy/userinfo`），核对手机号，并将稳定上游用户 ID 映射为本站的 `subject`。不能将手机号本身作为账号的唯一身份。
4. 若产品保留“短信注册后设置密码”，由适配器检查该上游账号是否已有用户自行设置的密码（当前为 root 路径的 `/smarterUsers/select/byId`）。尚未设置时才走上游设密接口，随后必须用新密码重新认证确认写入成功。不得将任何供应商的初始密码、固定请求头或项目标识复制到通用代码或浏览器。
5. 以 `provider ID + subject` 在本站创建或查找账号，签发本站随机会话。上游 token 仅用于当前服务器操作；短信、验证码和密码均不得写入本站数据库、浏览器存储或日志。

用一个局部的 provider 构造函数接收上述环境值和可注入的 `fetch`。它应统一处理超时、上游 5xx、非预期 JSON 与业务错误，向本站返回稳定的应用错误码，例如“未配置”“暂不可用”“验证码无效”“短信发送失败”“凭据无效”。不要把上游原始错误正文暴露给用户。

## 本站会话与首次设密

新项目维护自己的账号表、会话表、限流记录和首次设密记录。可沿用以下经验证的形态：

- 账号会话使用至少 256 位随机 token；数据库只保存 SHA-256 哈希。Cookie 设置为 `HttpOnly`、`Secure`（生产环境）、`SameSite=Strict`，并限制到本站 API 路径。
- 会话有效期和续期策略由新项目明确决定；若采用长期登录，可按实际数据库到期时间滑动续期，且只在本站数据库中续期，不因页面刷新反复调用上游登录。
- 短信验证成功但尚需设密码时，保留一个短期（Study Buddy 为 10 分钟）的一次性设密票据。票据绑定原访客、数据库仅存其哈希；暂存的上游凭据需加密保存，成功升级账号后删除。票据过期、篡改或访客不一致均必须拒绝。
- 首次设密并发执行时锁定对应短期记录。若上游已提交但响应丢失，重试先验证密码已经生效，不能盲目再次覆盖。
- 登出只撤销当前本站会话；是否提供全设备登出是另行明确的产品需求。

如需要保留未登录可浏览、登录后才能执行的体验，使用统一登录拦截层记录被打断的触发元素；登录窗口关闭后恢复仍存在的焦点。业务 API、WebSocket 和写操作仍由服务端强制要求账号，不能信任前端的“已登录”状态。

## 滥用与成本控制

短信是有成本的外部副作用。用数据库事务在调用供应商前预占配额，使多实例共享计数。Study Buddy 当前的起点是：同手机号 60 秒 1 条、24 小时 5 条；同访客 24 小时 10 条；同供应商 24 小时 200 条；密码登录、验证码校验和设密合计同一身份标识 15 分钟 10 次。新项目应根据供应商成本、目标用户和法规确认是否沿用这些数值；不应完全依赖客户端倒计时。

本站身份接口还应限制请求体、对写请求检查 Origin、设置 `Cache-Control: no-store`，并使用明确的失败码区分限流、无效验证码、未配置和上游故障。不要把手机号、验证码、密码、上游 token 或完整供应商响应写入诊断。

## 界面与验收

登录窗至少提供验证码登录；若启用密码登录和首次设密，应使用正确的浏览器输入语义：手机号 `autocomplete="username"`，验证码 `autocomplete="one-time-code"`，已有密码 `current-password`，首次密码 `new-password`。验证码输入允许粘贴；客户端倒计时只是提示，服务端限流才是权威。

实现不能止于界面。至少验证：

1. 适配器的成功、上游协议错误、超时、资料手机号不匹配、已有密码与首次设密分支；使用可控的 fetch 替身，不记录真实凭据。
2. 真实 PostgreSQL 下的本站会话哈希存储、账号隔离、短期票据的过期和绑定、并发限流、重复或丢失响应后的设密恢复。
3. 浏览器中的验证码注册／登录、必要的首次设密、退出、密码登录和未登录业务拒绝。
4. 仅在负责人明确授权、使用合成手机号并已说明费用时，做一次真实供应商短信验收；自动化通过或 mock 成功不替代这一步。

新项目应在自己的 `identity.md` 记录获准的服务合同、本站接口、Cookie／会话策略、已完成的验证和仍未做的真实供应商验收。不要将上游协议的猜测、凭据或用户资料写入该文档。
