Add login recovery contract and task card baseline
Why: The 2026-10-13 login flow plan requires a frozen contract record (three adjudications against the r2 proposal) and a traceable task card before implementation merges. Evidence: doc-only commit; acceptance matrix cites backend/frontend test files added in companion commits (SELF). Impact: docs/svc-deploy/2026-10-13-login-recovery-contract.md becomes the authoritative login-recovery contract; task card records user authorization and requirement table L1-L9. Refs: modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md r2
This commit is contained in:
@@ -7,7 +7,11 @@
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3fb8210f6495a78e70a67af3661bdfe9c00fb9de
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-login-recovery-flow(worktree);分支:feature/svc-login-recovery-flow
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 授权与交付终点:用户 2026-10-13 提交《登录流程方案》全文并指示「按此方案实施」——
|
||||
该指示即方案人工批准与实施授权。终点 = Backend 事务补强与管理员签发接口、Frontend
|
||||
登录恢复接线、Dev 合同文档与 gitlink 组合、PR 合入(智能体 Review 与合入按
|
||||
policy/delivery.md 执行)。未授权:目标环境部署与 live 账户操作(验收矩阵中
|
||||
浏览器 E2E、SSE 断流等遗留项另行任务)。
|
||||
- 交付模式:manual(前序人工决策,智能体 Review/合入);其他阶段明确授权后可记为 authorized-continuous
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 自动推进授权:无;授权范围:无;授权来源:无;授权终点:无;停止条件:无(未授权)
|
||||
@@ -28,13 +32,25 @@
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
| L1 | 密码登录为默认入口;登录建会话绑定认证版本 | 符合;既有 login + 新增 auth_version(迁移 v28) | P0 | 竞态测试:重置后旧版本建会话被拒 | 迁移 v28 | 纳入本增量 |
|
||||
| L2 | 管理员网页签发/状态/撤销一次性登录 Key | 需调整;原为直接重置密码,裁定改为签发 Key | P0 | 非管理员/受限会话 403;明文仅签发响应一次 | 后端 admin 路由 | 纳入本增量 |
|
||||
| L3 | 兑换只输 Key;受限会话只开身份/设密/退出 | 符合;既有 redeem + allowlist,补 IP 退避 | P0 | 100 并发兑换单胜;业务/管理入口 403 | — | 纳入本增量 |
|
||||
| L4 | 恢复设密免当前密码;完成回登录页 | 需调整;前端门去当前密码框,onDone 不再直放行 | P0 | reloginRequired=true → 登录页;刷新按 sessionScope 恢复门 | L1 | 纳入本增量 |
|
||||
| L5 | 主动改密要求当前密码且单事务 | 需调整;原三语句非事务 | P0 | 缺/错 current 401;当前会话保留其他吊销 | — | 纳入本增量 |
|
||||
| L6 | 补发/重置使旧 Key 与受限会话失效 | 需补齐;签发原不动已兑换会话 | P0 | 补发后旧会话 401、旧 Key 不可兑换 | — | 纳入本增量 |
|
||||
| L7 | 登录页移除 ask_ 日常入口;管理员 UI 改签发 | 需调整(用户裁定) | P1 | 无 ask_ tab;用户管理为签发 Key 面板 | L2 | 纳入本增量 |
|
||||
| L8 | 账户事件审计与密码变更同事务 | 需补齐;新增 account_events(v28) | P1 | issued/redeemed/password_set 等事件入库 | 迁移 v28 | 纳入本增量 |
|
||||
| L9 | 浏览器兑换专用 E2E、SSE 断流、PG 双库 | 部分;PG 经 CI test-pg,E2E/SSE 遗留 | P2 | 合同文档遗留项清单 | CI | 后续处理 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
- 方案引用:用户 2026-10-13《登录流程方案》(会话全文)=需求与方案主记录;
|
||||
合同定稿 docs/svc-deploy/2026-10-13-login-recovery-contract.md(本任务产出,含三裁定与验收矩阵)
|
||||
- 实施计划引用:方案第八节 A0–A5;本增量执行 A0(合同文档)+ A1(后端事务)+
|
||||
A2(管理员接口)+ A3(前端接线);A4 组合验证 = Dev make test + CI;A5 = PR 合入
|
||||
- 测试计划引用:Backend internal/store/login_keys_test.go(8 用例,openTestStore 双库)、
|
||||
internal/server/login_recovery_test.go(4 HTTP 用例);Frontend test/loginpage.test.ts
|
||||
(兑换文案)、e2e/changepw-form.playwright.ts(门形态更新);GATE 以 PR HEAD CI 为准
|
||||
|
||||
## 阶段评审
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 登录恢复流程合同(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)为准。
|
||||
|
||||
## 遗留项(不阻塞本合同)
|
||||
|
||||
- 登录页专用兑换 E2E(login-key.playwright.ts,U5 计划文件)。
|
||||
- SSE 长连接的认证版本主动复验/断流通知。
|
||||
- Documents 主方案 r2 的 U4/U5 规格按本文回写(与 PR #87 合入后再推进,避免文档冲突)。
|
||||
- 兑换入口跨实例限流(当前进程内退避,与 `loginGuard` 同档)。
|
||||
Reference in New Issue
Block a user