Files
Genarrative/docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md

5.6 KiB
Raw Blame History

/api/auth/logout 当前会话吊销落地设计

日期:2026-04-21

1. 文档目的

这份文档用于指导 M2实现会话吊销 任务的第一段首版落地,冻结:

  1. POST /api/auth/logout 的请求与响应 contract。
  2. 当前设备退出时 refresh session 吊销与 token_version 递增的组合语义。
  3. Rust 首版在进程内鉴权真相中的最小实现边界。
  4. 与后续 logout-allsessions/: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 成功响应

{
  "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. 文档、任务清单与测试已同步更新