Files
Agent-System-Dev/docs/svc-deploy/2026-10-13-login-recovery-contract.md
T
weichen d518b3eab2 Close the login recovery contract leftovers in Dev
Why: The remaining open items (API-key SSE closure, cross-instance
redeem backoff, U7 atomic bootstrap/create, CLI credential delivery)
landed in Backend #192; Dev pins it and closes the contract leftovers.

Evidence: Backend #192 CI success at head bc1e0a3 (GATE); composite CI
on this PR re-runs the suite incl. test-pg (GATE).

Impact: backend pin cfb8308; contract leftover section now records all
items closed; task card H1-H6 requirement table.

Refs: Backend #192; docs/svc-deploy/2026-10-13-login-recovery-contract.md.
2026-10-13 06:07:57 +00:00

6.5 KiB
Raw Blame History

登录恢复流程合同(2026-10-13)

状态:已实施(Dev 任务 feature/svc-login-recovery-flow;用户 2026-10-13「按此方案实施」授权)。 上游方案:modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md(r2); 本文是该方案登录部分的合同定稿与三项裁定记录,U4/U5 规格后续以本文为准回写。

登录初衷(冻结)

  1. 日常使用用户名 + 密码登录。
  2. 忘记密码时,由管理员签发绑定该用户的一次性登录 Key(alk_)。
  3. 用户兑换 Key 后进入受限设密会话,必须设置新密码;恢复设密不要求旧密码。
  4. 个人设置中的主动改密仍要求当前密码。
  5. 受限会话只开放身份查询、设密、退出;完成前不能进入工作台或访问业务接口。

三项裁定(对 r2 的合同分歧,用户按新方案裁定)

分歧 r2 表述 裁定(本合同)
登录页长期 API 密钥入口 保留并标注集成用途 移除日常登录入口;程序化客户端直接带 Authorization 头。浏览器不再持久保存 ask_
管理员网页恢复方式 直接设置新密码(CLI 保留) 网页改为签发一次性登录 Key;旧 POST /api/admin/users/{owner}/password 接口兼容保留(调用盘点:仅前端 UsersPanel 使用,已下线)
must-change 首登会话 full 会话 + 中间件动态拦截 登录即建 set_password_only 受限会话,与恢复会话同形;me.sessionScope 统一驱动前端设密门

接口合同

接口 状态 行为
POST /api/auth/login 已有(修订) 密码验证绑定认证版本;must-change/未设密建受限会话;响应带 sessionScope
POST /api/auth/login-key/redeem 已有 请求只有 {key};身份由服务端绑定;按来源 IP 退避(10 次/30s)
GET /api/me 已有 返回 sessionScope/authVia/mustChangePassword/passwordSet
POST /api/auth/password 已有(修订) 受限会话免当前密码(CompleteSetPasswordChecked,事务内复验);主动改密要求当前(单事务)
POST /api/auth/logout 已有 注销当前会话
POST /api/admin/users/{owner}/login-keys 新增 管理员签发;明文仅响应一次;补发同事务撤销旧 Key 与受限会话
GET /api/admin/users/{owner}/login-keys 新增 状态/有效期列表,无明文
DELETE /api/admin/users/{owner}/login-keys/{id} 新增 撤销 Key 并作废该用户受限会话

统一字段:sessionScope(full|set_password_only)、mustChangePassword、passwordSet、reloginRequired。 恢复设密成功固定返回登录页(reloginRequired=true),用新密码重新登录。

事务与安全要求(对应方案第六节)

  1. 签发不改密码、不吊销正常会话、不动业务数据;补发使旧 Key 与其受限会话立即失效。
  2. 兑换与建会话原子提交;会话绑定签发时点的 users.auth_version。
  3. 设密事务内复验会话(存在/未过期/scope/版本),统一写密码、清标志、bump 版本、 撤 Key、删会话、写审计(account_events,v28 迁移)。
  4. 密码登录会话创建校验认证版本,堵「旧密码验证在途 → 恢复完成 → 旧请求建会话」。
  5. 主动改密单事务(校验当前密码 → 更新 → 吊销其他会话保留当前)。
  6. 每次密码写入 bump auth_version 并作废全部旧会话;管理员/CLI 重置同事务。
  7. 会话 cookie 在 TLS(直连或 X-Forwarded-Proto: https)下置 Secure。
  8. SSE 长连接:恢复完成后旧会话行已删除,新请求即失效;存量连接的主动断流 依赖后续会话失效推送(本合同未实现,遗留项见下)。

验收矩阵与现状

项 结果 依据
正常登录/错误凭据 PASS(SQLite) login_recovery_test.go、既有 e2e
只输 Key 识别账户、不能指定身份 PASS TestAdminLoginKeyRecoveryFlow
受限会话拒业务/管理员/签发入口 PASS 同上 + 中间件 allowlist
100 并发兑换恰好一次成功 PASS TestLoginKeyConcurrentRedeem
补发/撤销使旧 Key 与受限会话失效 PASS TestIssueLoginKeyRevokesDerivedSessions、TestAdminLoginKeyReissueAndRevoke
改密事务复验(被撤销会话不能改密) PASS TestCompleteSetPasswordRevalidatesSession
登录竞态(旧版本建会话被拒) PASS TestLoginRaceBlockedByAuthVersion
主动改密缺/错当前密码拒绝 PASS TestActiveChangePasswordRequiresCurrent
must-change 首登受限会话 PASS TestLoginMustChangeCreatesRestrictedSession
PostgreSQL 双库 GATE(CI test-pg job) store 测试走 openTestStore,AGENT_PG_TEST_DSN 下自动切真库
浏览器 E2E(兑换→设密→重登) NOT_RUN(本轮) e2e/changepw-form.playwright.ts 已更新门形态;专用兑换 E2E 遗留
SSE 存量连接主动断流 NOT_RUN(遗留) 见「遗留项」

以上本地结果均为 SELF;GATE 以 PR HEAD 的 CI(含 test-pg)为准。

遗留项(2026-10-13 全部收口)

  • 登录页专用兑换 E2E:已补齐(Frontend #77,e2e/login-key.playwright.ts; 真实浏览器全流程验收 PASS——签发→只输 Key 兑换→免旧密码设密→新密码登录→重复兑换被拒, 隔离 auth-on 实例,收据 sha256 be7ae9a2…/b3a9e691… 绑定 Dev HEAD 268ef29,SELF)。
  • SSE 长连接断流:已实现(Backend #189 会话凭证;#192 补齐 API key 凭证)—— 心跳点复验,失效即以 session-expired 终止(默认 15s,测试可缩短)。
  • Documents 主方案回写:已完成(Documents #94 r3,含三裁定与实施记录)。
  • 兑换入口跨实例限流:已实现(Backend #192,迁移 v29 redeem_attempts 共享库 窗口计数;双 Store 连接同库共担限流的测试覆盖)。
  • U7 余量:原子开户/引导、CLI 凭据安全交付、PG 锁串行首管:已实现 (Backend #192)——CreateAccount 单行 INSERT(并发同名 1 胜且胜者凭据不被覆盖); BootstrapFirstAdmin PG 咨询锁 + SQLite 插入后复查,建号+激活 Key+审计同事务, 已有用户拒绝接管(R11);CLI AGENT_SYSTEM_DSN 环境输入与 --out 0600 文件交付。
  • 部署与 live 账户验收:本任务执行部署与可验证项;owner 浏览器输密码一步 按合同由用户完成(密码不交给 Agent)。