# platform-auth refresh cookie 适配设计 日期:`2026-04-21` ## 1. 文档目的 这份文档用于指导 `M2` 中“实现 refresh cookie 读取”这条任务落地,目标是先把 refresh cookie 的读取边界、配置口径与 `api-server` 的最小接线固定下来。 本阶段只解决: 1. `platform-auth` 如何统一读取 refresh cookie。 2. `api-server` 如何把读取结果挂到请求上下文。 本阶段不解决: 1. refresh token 轮换。 2. refresh session 吊销。 3. refresh cookie 写回浏览器。 ## 2. 设计输入 本任务直接受以下文档约束: 1. [SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md) 2. [SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md](./SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md) 关键冻结点: 1. 浏览器 cookie 只存原始 refresh token。 2. 服务端真相表只存 `sha256(refresh_token)`。 3. cookie 名默认是 `genarrative_refresh_session`。 4. cookie `Path` 默认是 `/api/auth`。 5. 默认 `SameSite=Lax`。 ## 3. crate 边界 ### 3.1 `platform-auth` 负责: 1. 统一 refresh cookie 配置结构。 2. 统一 cookie 名、Path、SameSite、Secure、TTL 的平台配置。 3. 从 `Cookie` 请求头解析当前 refresh token。 不负责: 1. 直接操作 `Set-Cookie` 响应头。 2. 决定请求是否应该被视为已登录。 3. 访问 `refresh_session` 真相表。 ### 3.2 `api-server` 负责: 1. 从环境变量读取 refresh cookie 配置。 2. 在启动时构造唯一一份 `RefreshCookieConfig`。 3. 在请求链中读取 refresh cookie 并挂到 request extensions。 4. 为阶段验收提供最小内部调试入口。 不负责: 1. 重写一套独立 cookie 解析逻辑。 2. 在这一步直接实现 `/api/auth/refresh`。 ## 4. 配置口径 当前阶段 `api-server` 读取以下环境变量: | 配置项 | 环境变量 | 默认值 | 说明 | | --- | --- | --- | --- | | cookie 名 | `AUTH_REFRESH_COOKIE_NAME` | `genarrative_refresh_session` | 读取 refresh cookie 时匹配的键名。 | | cookie path | `AUTH_REFRESH_COOKIE_PATH` | `/api/auth` | 当前阶段只用于冻结配置口径。 | | cookie secure | `AUTH_REFRESH_COOKIE_SECURE` | `false` | 当前阶段只读配置,不在读取路径参与判断。 | | cookie same-site | `AUTH_REFRESH_COOKIE_SAME_SITE` | `Lax` | 固定枚举为 `Lax`、`Strict`、`None`。 | | session TTL 天数 | `AUTH_REFRESH_SESSION_TTL_DAYS` | `30` | 当前阶段只读配置,不参与读取判断。 | 说明: 1. 这组变量直接对齐当前 Node 口径。 2. 本阶段读取链路只真正依赖 `cookie_name`,其余配置主要用于冻结统一初始化边界。 ## 5. Rust 结构设计 ### 5.1 `RefreshCookieSameSite` 用途: 1. 避免不同 crate 手写 `Lax` / `Strict` / `None` 字符串。 2. 在启动阶段尽早拒绝非法配置。 ### 5.2 `RefreshCookieConfig` 字段: 1. `cookie_name` 2. `cookie_path` 3. `cookie_secure` 4. `cookie_same_site` 5. `refresh_session_ttl_days` 约束: 1. `cookie_name` 不能为空。 2. `cookie_path` 不能为空。 3. `refresh_session_ttl_days` 必须大于 `0`。 ## 6. 读取规则 `read_refresh_session_token(cookie_header, config)` 固定按以下规则工作: 1. 若 `Cookie` 头为空,返回 `None`。 2. 按 `;` 分割各 cookie 项。 3. 只匹配与 `config.cookie_name` 完全相等的键名。 4. 若匹配项值为空,返回 `None`。 5. 对匹配值执行 URL decode。 6. decode 成功则返回原始 refresh token,失败则返回 `None`。 说明: 1. 当前阶段读取逻辑只做“解析”,不做权限判断。 2. 请求是否能继续 refresh,要在后续应用层结合 `refresh_session` 真相表判断。 ## 7. api-server 最小接线 本阶段 `api-server` 只做三件事: 1. `AppConfig` 增加 refresh cookie 配置。 2. `AppState` 启动时构造 `RefreshCookieConfig`。 3. `attach_refresh_session_token` 中间件从请求头读取 cookie,并把结果以 `RefreshSessionToken` 写入 request extensions。 阶段验收入口: 1. `/_internal/auth/refresh-cookie` 该入口仅返回: 1. `cookieName` 2. `present` 3. `tokenLength` 说明: 1. 当前不回显原始 refresh token,避免把敏感值直接暴露到响应体中。 2. `tokenLength` 只用于阶段测试与调试,不是最终对外 contract。 ## 8. 测试策略 当前阶段至少覆盖: 1. 纯函数能正确提取目标 cookie。 2. URL 编码的 cookie 值能正确解码。 3. 缺少目标 cookie 时返回空。 4. `api-server` 在无 cookie 时能返回 `present = false`。 5. `api-server` 在有 cookie 时能返回 `present = true`。 ## 9. 完成定义 满足以下条件时,本任务视为完成: 1. `platform-auth` 中存在真实可编译的 refresh cookie 配置与读取逻辑。 2. `api-server` 已接入统一 refresh cookie 配置。 3. 请求链已能读取 cookie 并挂到 extensions。 4. 编译与测试通过。 5. 任务清单已同步更新。 ## 10. 后续衔接 这条任务完成后,下一步继续衔接: 1. refresh token 哈希与 `refresh_session` 表查询。 2. refresh token 轮换。 3. `/api/auth/refresh` 正式接口。 4. `/api/auth/logout`、`/api/auth/logout-all` 的会话吊销。