feat: add current session logout flow
This commit is contained in:
209
docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md
Normal file
209
docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# `/api/auth/logout` 当前会话吊销落地设计
|
||||
|
||||
日期:`2026-04-21`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档用于指导 `M2` 中 `实现会话吊销` 任务的第一段首版落地,冻结:
|
||||
|
||||
1. `POST /api/auth/logout` 的请求与响应 contract。
|
||||
2. 当前设备退出时 refresh session 吊销与 `token_version` 递增的组合语义。
|
||||
3. Rust 首版在进程内鉴权真相中的最小实现边界。
|
||||
4. 与后续 `logout-all`、`sessions/:sessionId/revoke` 的职责切分。
|
||||
|
||||
## 2. 当前基线
|
||||
|
||||
当前 Node `/api/auth/logout` 已具备以下稳定语义:
|
||||
|
||||
1. 必须先通过 Bearer JWT 校验。
|
||||
2. 从 cookie 中读取当前 refresh token,并尝试吊销对应 refresh session。
|
||||
3. 无论当前 refresh session 是否已存在,只要用户存在,仍继续执行“退出当前设备”。
|
||||
4. 对当前用户执行 `token_version + 1`,使当前 access token 全局失效。
|
||||
5. 响应成功时始终清理 refresh cookie。
|
||||
|
||||
因此,Node 的“退出当前设备”实际是两层组合动作:
|
||||
|
||||
1. 设备级:吊销当前 refresh session
|
||||
2. 用户级:递增 `token_version`,让当前 access token 立即失效
|
||||
|
||||
Rust 首版必须保留这个语义。
|
||||
|
||||
## 3. 当前阶段范围
|
||||
|
||||
本阶段只落以下内容:
|
||||
|
||||
1. `module-auth` 增加当前 refresh session 吊销能力。
|
||||
2. `module-auth` 增加用户 `token_version` 递增能力。
|
||||
3. `api-server` 暴露 `POST /api/auth/logout`。
|
||||
4. 成功或已失效场景统一清理 refresh cookie。
|
||||
|
||||
本阶段明确不包含:
|
||||
|
||||
1. `/api/auth/logout-all`
|
||||
2. `/api/auth/sessions`
|
||||
3. `/api/auth/sessions/:sessionId/revoke`
|
||||
4. 审计日志与风控日志正式落表
|
||||
5. SpacetimeDB reducer 真正写表
|
||||
|
||||
## 4. contract
|
||||
|
||||
### 4.1 请求
|
||||
|
||||
1. 方法:`POST`
|
||||
2. 路径:`/api/auth/logout`
|
||||
3. 请求体:空
|
||||
4. 鉴权:
|
||||
- Bearer JWT 必填
|
||||
- refresh cookie 选填但应尽量提供
|
||||
|
||||
### 4.2 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
同时响应头必须写回清理后的 refresh cookie。
|
||||
|
||||
### 4.3 失败响应
|
||||
|
||||
以下情况返回 `401 UNAUTHORIZED`:
|
||||
|
||||
1. Bearer JWT 缺失或非法
|
||||
2. JWT 对应用户不存在
|
||||
|
||||
说明:
|
||||
|
||||
1. 当前 refresh cookie 缺失本身不构成 `/logout` 失败。
|
||||
2. 因为当前设备可能已经没有 refresh cookie,但 access token 仍应允许执行显式退出。
|
||||
|
||||
## 5. 固定语义
|
||||
|
||||
### 5.1 当前设备退出的动作顺序
|
||||
|
||||
`POST /api/auth/logout` 固定按以下顺序执行:
|
||||
|
||||
1. 从 Bearer JWT 解析当前用户。
|
||||
2. 尝试按当前 refresh cookie 吊销 refresh session。
|
||||
3. 对当前用户执行 `token_version + 1`。
|
||||
4. 返回 `ok: true`。
|
||||
5. 始终清理 refresh cookie。
|
||||
|
||||
### 5.2 refresh session 吊销是“尽力而为”
|
||||
|
||||
当 refresh cookie 缺失、refresh token 无法命中 session、session 已吊销时:
|
||||
|
||||
1. 不把这些情况视为 `/logout` 失败。
|
||||
2. 继续执行用户级 `token_version` 递增。
|
||||
|
||||
原因:
|
||||
|
||||
1. 当前设备退出的主目标是让“现在这份 access token”立刻失效。
|
||||
2. refresh session 丢失不应该阻断显式退出。
|
||||
|
||||
### 5.3 `token_version` 必须递增
|
||||
|
||||
当前阶段固定规则:
|
||||
|
||||
1. `/logout` 必须递增 `user.token_version`
|
||||
2. 后续 Bearer JWT 校验必须比对当前用户最新 `token_version`
|
||||
|
||||
说明:
|
||||
|
||||
1. 如果不递增,当前 access token 直到自然过期前仍可继续访问。
|
||||
2. 这与 Node 当前行为不一致,也会让“退出登录”在用户感知上失真。
|
||||
|
||||
## 6. 与其他接口的职责切分
|
||||
|
||||
### 6.1 `/api/auth/logout`
|
||||
|
||||
负责:
|
||||
|
||||
1. 当前设备退出
|
||||
2. 当前 access token 立即失效
|
||||
3. 当前 refresh session 尽力吊销
|
||||
|
||||
### 6.2 `/api/auth/logout-all`
|
||||
|
||||
后续负责:
|
||||
|
||||
1. 吊销同一用户全部 refresh session
|
||||
2. 递增一次 `token_version`
|
||||
|
||||
### 6.3 `/api/auth/sessions/:sessionId/revoke`
|
||||
|
||||
后续负责:
|
||||
|
||||
1. 只吊销指定远端设备 refresh session
|
||||
2. 不递增 `token_version`
|
||||
|
||||
## 7. crate 边界
|
||||
|
||||
### 7.1 `module-auth`
|
||||
|
||||
负责:
|
||||
|
||||
1. 按 refresh token hash 吊销当前 session。
|
||||
2. 递增当前用户 `token_version`。
|
||||
3. 返回退出后最新用户快照,供后续 access token 校验使用。
|
||||
|
||||
### 7.2 `platform-auth`
|
||||
|
||||
负责:
|
||||
|
||||
1. refresh token 哈希
|
||||
2. 构造清理 cookie 的 `Set-Cookie` 头
|
||||
|
||||
### 7.3 `api-server`
|
||||
|
||||
负责:
|
||||
|
||||
1. Bearer JWT 与 refresh cookie 的读取
|
||||
2. 调用 `module-auth` 组合执行当前设备退出
|
||||
3. 始终回写清理 cookie
|
||||
|
||||
## 8. 进程内实现策略
|
||||
|
||||
当前阶段 `module-auth` 继续使用进程内真相,新增以下最小能力:
|
||||
|
||||
1. `revoke_session_by_refresh_token_hash`
|
||||
2. `increment_user_token_version`
|
||||
|
||||
其中:
|
||||
|
||||
1. session 吊销要写入 `revoked_at`
|
||||
2. 用户版本递增要直接修改内存中用户快照
|
||||
|
||||
## 9. Bearer JWT 校验补强
|
||||
|
||||
为了让 `/logout` 后“旧 access token 立即失效”真正成立,当前阶段需要补一条约束:
|
||||
|
||||
1. Bearer JWT 校验通过签名后,还必须比对 claims 里的 `ver`
|
||||
2. 若 `claims.ver != 当前用户 token_version`,返回 `401`
|
||||
|
||||
说明:
|
||||
|
||||
1. 这是当前 Rust 鉴权链路必须补上的一致性校验。
|
||||
2. 否则 `logout` 虽然递增了用户版本,但旧 JWT 仍能继续访问。
|
||||
|
||||
## 10. 测试策略
|
||||
|
||||
至少覆盖:
|
||||
|
||||
1. 登录成功后调用 `/api/auth/logout` 返回 `ok: true`
|
||||
2. `/logout` 成功后会清理 refresh cookie
|
||||
3. `/logout` 成功后旧 Bearer token 再访问 `/api/auth/me` 返回 `401`
|
||||
4. refresh cookie 缺失时,只要 Bearer token 有效,`/logout` 仍返回 `ok: true`
|
||||
5. 用户不存在时 `/logout` 返回 `401`
|
||||
|
||||
## 11. 完成定义
|
||||
|
||||
满足以下条件时,本任务视为完成:
|
||||
|
||||
1. Rust 侧已提供 `POST /api/auth/logout`
|
||||
2. 当前 refresh session 可按 cookie 对应关系被吊销
|
||||
3. 用户 `token_version` 会在退出时递增
|
||||
4. Bearer JWT 已补充版本比对
|
||||
5. `/logout` 总会清理 refresh cookie
|
||||
6. 文档、任务清单与测试已同步更新
|
||||
Reference in New Issue
Block a user