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

185 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `/api/auth/sessions` 会话列表与多端标识查询设计
日期:`2026-04-21`
## 1. 文档目的
这份文档用于指导 `M2``兼容 /api/auth/sessions` 的首版落地,冻结:
1. `GET /api/auth/sessions` 的请求与响应 contract
2. 当前设备识别方式与 `isCurrent` 语义
3. 多端登录识别字段如何从 `refresh_session` 派生到 DTO
4. Rust 首版在 Axum + 进程内 `module-auth` 下的最小实现边界
## 2. 当前基线
当前 Node `/api/auth/sessions` 已具备以下稳定行为:
1. 依赖 Bearer JWT 确认用户身份
2. 从 refresh cookie 识别当前设备
3. 返回当前账号全部未吊销活跃会话
4. 每条记录给出端侧标签、最近活跃时间、到期时间、IP 脱敏信息与是否当前设备
当前问题是:
1. 旧实现只能粗略给出“网页端浏览器 / 移动端浏览器”
2. 无法稳定区分同设备不同浏览器
3. 无法区分微信内 H5 与微信小程序、小程序平台来源
因此本次 `/api/auth/sessions` 首版落地必须直接承接多端会话身份模型。
## 3. 设计输入
本任务直接受以下文档约束:
1. [MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md](./MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md)
2. [SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md)
3. [AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md](./AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md)
4. [AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md](./AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md)
## 4. 首版落地范围
本阶段只落以下内容:
1. `module-auth` 提供按 `user_id` 读取活跃 refresh session 列表的能力
2. `api-server` 暴露 `GET /api/auth/sessions`
3. 登录创建 session 时落库结构化客户端身份字段
4. 会话列表返回多端识别所需字段,并兼容旧 `clientLabel`
本阶段明确不包含:
1. `/api/auth/sessions/:sessionId/revoke`
2. 前端完整消费全部新增字段
3. SpacetimeDB reducer / view 正式读表
## 5. 请求与响应 contract
### 5.1 请求
1. 方法:`GET`
2. 路径:`/api/auth/sessions`
3. 请求体:空
4. 鉴权:
- Bearer JWT 必填
- refresh cookie 选填但建议携带,用于判断 `isCurrent`
### 5.2 成功响应
```json
{
"sessions": [
{
"sessionId": "usess_xxx",
"clientType": "web_browser",
"clientRuntime": "chrome",
"clientPlatform": "windows",
"clientLabel": "Windows / Chrome",
"deviceDisplayName": "Windows / Chrome",
"miniProgramAppId": null,
"miniProgramEnv": null,
"userAgent": "Mozilla/5.0 ...",
"ipMasked": "203.0.*.*",
"isCurrent": true,
"createdAt": "2026-04-21T10:00:00Z",
"lastSeenAt": "2026-04-21T10:05:00Z",
"expiresAt": "2026-05-21T10:00:00Z"
}
]
}
```
字段说明:
1. `clientLabel` 当前阶段继续兼容旧前端字段,值固定与 `deviceDisplayName` 保持一致
2. `clientRuntime``clientPlatform``deviceDisplayName` 是多端识别首版最小新增字段
3. 小程序来源额外暴露 `miniProgramAppId``miniProgramEnv`
### 5.3 失败响应
以下情况返回 `401 UNAUTHORIZED`
1. Bearer JWT 缺失或非法
2. Bearer JWT 对应用户不存在
仓储读取失败返回 `500 INTERNAL_SERVER_ERROR`
## 6. 当前设备识别规则
`isCurrent` 固定按以下规则判断:
1. 从 refresh cookie 读取当前原始 refresh token
2. 在 Axum 侧计算 `sha256(refresh_token)`
3. 与会话列表中的 `refresh_token_hash` 比较
4. 命中则 `isCurrent = true`
说明:
1. 如果请求没有携带 refresh cookie本接口仍可返回会话列表
2. 此时全部会话的 `isCurrent` 都为 `false`
## 7. 多端标识派生规则
### 7.1 后端入库字段
登录创建会话时Axum 必须先解析并写入:
1. `client_type`
2. `client_runtime`
3. `client_platform`
4. `client_instance_id`
5. `device_fingerprint`
6. `device_display_name`
7. `mini_program_app_id`
8. `mini_program_env`
9. `user_agent`
10. `ip`
### 7.2 DTO 派生规则
会话列表返回时:
1. `clientType = client_type`
2. `clientRuntime = client_runtime`
3. `clientPlatform = client_platform`
4. `deviceDisplayName = device_display_name`
5. `clientLabel = device_display_name`
6. `miniProgramAppId = mini_program_app_id`
7. `miniProgramEnv = mini_program_env`
## 8. crate 边界
### 8.1 `module-auth`
负责:
1. 保存 refresh session 客户端身份快照
2.`user_id` 返回活跃会话列表
3. 保持 refresh 轮换后 `session_id` 稳定、客户端身份字段不漂移
### 8.2 `api-server`
负责:
1. 读取 Bearer JWT 与 refresh cookie
2. 把活跃会话映射成旧接口兼容 DTO
3. 派生 `ipMasked``isCurrent`
## 9. 测试策略
至少覆盖:
1. 同一账号在同平台不同浏览器登录后,会话列表能返回两条不同运行时记录
2. 微信内 H5 登录后,会话列表返回 `wechat_h5 + wechat_embedded_browser`
3. 显式小程序头优先于 `User-Agent` 判断
4. 请求携带当前 refresh cookie 时,只有当前会话 `isCurrent = true`
## 10. 完成定义
满足以下条件时,本任务视为完成:
1. Rust 侧已提供 `GET /api/auth/sessions`
2. 会话列表可区分普通浏览器、微信内 H5、小程序来源
3. 同设备不同浏览器可在会话列表中清晰区分
4. `clientLabel` 与新增多端字段都已稳定返回
5. 文档、任务清单与测试已同步更新