7.7 KiB
7.7 KiB
/api/auth/sessions 会话列表与多端标识查询设计
日期:2026-04-21
1. 文档目的
这份文档用于指导 M2 中 兼容 /api/auth/sessions 的首版落地,冻结:
GET /api/auth/sessions的请求与响应 contract- 当前设备识别方式与
isCurrent语义 - 多端登录识别字段如何从
refresh_session派生到 DTO - Rust 首版在 Axum + 进程内
module-auth下的最小实现边界 2026-05-13会话组合并展示与远端踢下线闭环修复口径
2. 当前基线
当前 Node /api/auth/sessions 已具备以下稳定行为:
- 依赖 Bearer JWT 确认用户身份
- 从 refresh cookie 识别当前设备
- 返回当前账号全部未吊销活跃会话
- 每条记录给出端侧标签、最近活跃时间、到期时间、IP 脱敏信息与是否当前设备
当前问题是:
- 旧实现只能粗略给出“网页端浏览器 / 移动端浏览器”
- 无法稳定区分同设备不同浏览器
- 无法区分微信内 H5 与微信小程序、小程序平台来源
因此本次 /api/auth/sessions 首版落地必须直接承接多端会话身份模型。
3. 设计输入
本任务直接受以下文档约束:
- MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md
- SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md
- AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md
- AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md
4. 首版落地范围
本阶段只落以下内容:
module-auth提供按user_id读取活跃 refresh session 列表的能力api-server暴露GET /api/auth/sessions- 登录创建 session 时落库结构化客户端身份字段
- 会话列表返回多端识别所需字段,并兼容旧
clientLabel
2026-05-13 起,本接口同时承担账号安全页的会话组读模型:
- 后端按“同设备 + 同 IP”聚合活跃
refresh_session - 前端只消费后端聚合结果,不自行推断合并
POST /api/auth/sessions/{sessionId}/revoke已纳入 Rust 实现,用于踢下线非当前会话
本阶段仍明确不包含:
- SpacetimeDB reducer / view 正式读表
- 登录方式、refresh token 轮换策略或账号安全页整体重设计
5. 请求与响应 contract
5.1 请求
- 方法:
GET - 路径:
/api/auth/sessions - 请求体:空
- 鉴权:
- Bearer JWT 必填
- refresh cookie 选填但建议携带,用于判断
isCurrent
5.2 成功响应
{
"sessions": [
{
"sessionId": "usess_xxx",
"sessionIds": ["usess_xxx", "usess_yyy"],
"sessionCount": 2,
"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"
}
]
}
字段说明:
sessionId是聚合组代表会话 ID;若组内包含当前sid,代表 ID 必须使用当前会话 IDsessionIds是该聚合组内全部活跃 session ID,前端批量踢下线时逐个调用 revokesessionCount是聚合组内 session 数量clientLabel当前阶段继续兼容旧前端字段,值固定与deviceDisplayName保持一致clientRuntime、clientPlatform、deviceDisplayName是多端识别首版最小新增字段- 小程序来源额外暴露
miniProgramAppId、miniProgramEnv
5.3 失败响应
以下情况返回 401 UNAUTHORIZED:
- Bearer JWT 缺失或非法
- Bearer JWT 对应用户不存在
仓储读取失败返回 500 INTERNAL_SERVER_ERROR。
6. 当前设备识别规则
isCurrent 固定按以下规则判断:
- 从 refresh cookie 读取当前原始 refresh token
- 在 Axum 侧计算
sha256(refresh_token) - 与会话列表中的
refresh_token_hash比较 - 同时读取 Bearer access token claims 中的
sid - 聚合组内任意 session 命中当前 refresh hash 或当前
sid,则整组isCurrent = true
说明:
- 如果请求没有携带 refresh cookie,本接口仍可返回会话列表
- 此时仍可通过 Bearer
sid标记当前组 - 当前组不允许在前端显示“踢下线”,当前设备退出必须走
/api/auth/logout
6.1 会话组合并规则
同设备同 IP 的 active refresh sessions 在后端合并为一条 DTO:
- 优先使用
device_fingerprint + ip作为聚合 key - 无
device_fingerprint时退化为client_type + client_runtime + client_platform + device_display_name + user_agent + ip createdAt取组内最早created_atlastSeenAt取组内最新last_seen_atexpiresAt取组内最新expires_atipMasked仍只返回脱敏 IP
7. 多端标识派生规则
7.1 后端入库字段
登录创建会话时,Axum 必须先解析并写入:
client_typeclient_runtimeclient_platformclient_instance_iddevice_fingerprintdevice_display_namemini_program_app_idmini_program_envuser_agentip
7.2 DTO 派生规则
会话列表返回时:
clientType = client_typeclientRuntime = client_runtimeclientPlatform = client_platformdeviceDisplayName = device_display_nameclientLabel = device_display_nameminiProgramAppId = mini_program_app_idminiProgramEnv = mini_program_env
8. crate 边界
8.1 module-auth
负责:
- 保存 refresh session 客户端身份快照
- 按
user_id返回活跃会话列表 - 保持 refresh 轮换后
session_id稳定、客户端身份字段不漂移
8.2 api-server
负责:
- 读取 Bearer JWT 与 refresh cookie
- 按同设备同 IP 聚合活跃会话
- 把活跃会话组映射成旧接口兼容 DTO
- 派生
ipMasked与isCurrent - 暴露
POST /api/auth/sessions/{sessionId}/revoke
8.3 指定会话吊销接口
POST /api/auth/sessions/{sessionId}/revoke 固定规则:
- Bearer JWT 必填
- 仅允许吊销当前用户自己的非当前会话
- 当前会话自吊销返回业务错误,提示使用退出登录
- 只撤销目标
refresh_session,不递增token_version - 撤销后同步 auth store 到 SpacetimeDB
- 认证中间件会校验 access token
sid对应 activerefresh_session,因此被踢设备已有 access token 会立即失效
9. 测试策略
至少覆盖:
- 同一账号在同平台不同浏览器登录后,会话列表能返回两条不同运行时记录
- 微信内 H5 登录后,会话列表返回
wechat_h5 + wechat_embedded_browser - 显式小程序头优先于
User-Agent判断 - 请求携带当前 refresh cookie 时,只有当前会话
isCurrent = true - 同设备同 IP 会话会合并,并返回
sessionIds/sessionCount - 合并组包含当前
sid或当前 refresh hash 时,整组isCurrent = true - 指定远端会话吊销后,被踢设备 access token 立即无法通过认证
10. 完成定义
满足以下条件时,本任务视为完成:
- Rust 侧已提供
GET /api/auth/sessions - 会话列表可区分普通浏览器、微信内 H5、小程序来源
- 同设备不同浏览器可在会话列表中清晰区分
clientLabel与新增多端字段都已稳定返回- 同设备同 IP 的重复 active refresh sessions 已合并展示
- 非当前会话可通过真实 revoke 接口踢下线
- 文档、任务清单与测试已同步更新