diff --git a/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md b/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md index 524e9695..289e9a00 100644 --- a/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md +++ b/backend-rewrite-tasklist/01_M0_M2_FOUNDATION_AND_AUTH.md @@ -146,7 +146,8 @@ 交付物:[../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md) - [x] 设计 `refresh_session` 交付物:[../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md) -- [ ] 设计 `auth_audit_log` +- [x] 设计 `auth_audit_log` + 交付物:[../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md](../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md) - [ ] 设计 `auth_risk_block` - [ ] 设计 `sms_auth_event` - [ ] 设计 `wechat_auth_state` diff --git a/docs/technical/README.md b/docs/technical/README.md index bfcd05ff..01fb85bc 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -4,6 +4,7 @@ ## 文档列表 +- [SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md):`M2` 第四张鉴权审计表 `auth_audit_log` 的事件范围、追加写规则、索引与对外 DTO 派生约束。 - [SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md):`M2` 第三张会话表 `refresh_session` 的 cookie/hash 边界、轮换与吊销语义、索引与迁移规则。 - [SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md):`M2` 第二张身份表 `auth_identity` 的 provider 范围、唯一约束、手机号/微信身份写入规则与迁移策略。 - [SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md):`M2` 第一张身份主表 `user_account` 的职责边界、字段、唯一约束、状态迁移、旧 `users` 映射与落地约束。 diff --git a/docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md b/docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md new file mode 100644 index 00000000..c71d042e --- /dev/null +++ b/docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md @@ -0,0 +1,339 @@ +# `auth_audit_log` 表设计 + +日期:`2026-04-21` + +## 1. 文档目的 + +这份文档用于完成 `M2` 的第四条任务:`设计 auth_audit_log`。 + +目标是把鉴权审计表固定成一张“只追加的事实表”,并明确: + +1. 哪些鉴权动作必须写审计 +2. 审计表与风控表、会话表、账号表的边界 +3. 哪些字段应该入库,哪些字段应该在读取时派生 +4. `/api/auth/audit-logs` 要依赖什么读模型 + +## 2. 当前基线 + +当前 Node 后端已经存在 `auth_audit_logs` 表,并有完整写入链路: + +1. `authService.ts` 通过 `writeAuthAuditLog(...)` 统一写入 +2. `GET /api/auth/audit-logs` 会按用户倒序返回最近 20 条 +3. 前端拿到的是已经过展示加工的 DTO: + - `title` + - `detail` + - `ipMasked` + - `userAgent` + - `createdAt` + +当前 Node `auth_audit_logs` 表字段基线: + +1. `id` +2. `user_id` +3. `event_type` +4. `detail` +5. `ip` +6. `user_agent` +7. `meta_json` +8. `created_at` + +当前已使用的事件类型基线: + +1. `password_login` +2. `phone_login` +3. `wechat_login` +4. `wechat_bind_phone` +5. `change_phone` +6. `captcha_required` +7. `logout` +8. `logout_all` +9. `revoke_session` +10. `risk_block_phone` +11. `risk_block_ip` +12. `risk_unblock_phone` +13. `risk_unblock_ip` + +## 3. 表职责边界 + +### 3.1 `auth_audit_log` 负责 + +1. 记录“账号已经发生过什么安全相关动作” +2. 记录动作发生时的操作者账号 +3. 记录动作发生时的 IP、UA 与必要扩展上下文 +4. 作为 `/api/auth/audit-logs` 的唯一事实来源 + +### 3.2 它不负责 + +1. 风控封禁当前是否生效 +2. refresh session 当前是否活跃 +3. 短信验证码发送频控 +4. 账号当前状态 +5. 展示标题的最终本地化文案 + +### 3.3 与其他表的边界 + +1. `auth_risk_block` 负责“当前拦截状态” +2. `auth_audit_log` 负责“发生过封禁或解除动作的历史事实” +3. `refresh_session` 负责“当前设备会话” +4. `auth_audit_log` 负责“谁在什么时候移除了哪台设备” + +## 4. 访问级别 + +`auth_audit_log` 固定为 `private table`。 + +原因: + +1. 含用户行为安全记录 +2. 含原始 IP 与原始 UA +3. 含 provider 或设备相关元信息 + +对外读取固定通过: + +1. Axum 鉴权后按当前 `user_id` 查询 +2. 再由 view / service 转成脱敏 DTO + +## 5. 字段设计 + +| 字段名 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `audit_log_id` | `String` | 是 | 主键,建议继续沿用 `audit_*` 前缀。 | +| `user_id` | `String` | 是 | 归属账号 ID,外键指向 `user_account.user_id`。 | +| `event_type` | `String` | 是 | 事件类型枚举。 | +| `detail` | `String` | 是 | 当前事件的人类可读详情,继续保留中文。 | +| `ip` | `Option` | 否 | 事件发生时采集到的原始 IP。 | +| `user_agent` | `Option` | 否 | 事件发生时采集到的原始 UA。 | +| `meta_json` | `Option` | 否 | 扩展上下文 JSON 字符串,例如 `sessionId`、`targetIp`、`expiresAt`。 | +| `created_at` | `String` | 是 | 事件发生时间,UTC RFC3339。 | + +补充约束: + +1. 当前阶段不增加 `updated_at`,因为审计事件一旦写入就不允许回写。 +2. 当前阶段不单独存 `title`,继续由 `event_type` 在读取时派生。 +3. `detail` 继续保留中文事实描述,避免后续只剩事件码却缺少可读上下文。 + +## 6. 事件类型设计 + +当前阶段 `event_type` 固定只支持以下值: + +1. `password_login` +2. `phone_login` +3. `wechat_login` +4. `wechat_bind_phone` +5. `change_phone` +6. `captcha_required` +7. `logout` +8. `logout_all` +9. `revoke_session` +10. `risk_block_phone` +11. `risk_block_ip` +12. `risk_unblock_phone` +13. `risk_unblock_ip` + +当前不新增更细子类型,原因: + +1. 现有前端与 Node 展示已经围绕这 13 类建立 +2. M2 重点是兼容迁移,不是先重做一套新的安全事件 taxonomy + +后续若要扩展,只允许追加,不重命名已有事件码。 + +## 7. 展示派生规则 + +### 7.1 `title` 不入库 + +`/api/auth/audit-logs` 返回的 `title` 固定按 `event_type` 派生。 + +当前派生规则与现有 Node 对齐: + +1. `password_login` -> `账号密码登录` +2. `phone_login` -> `手机号登录` +3. `wechat_login` -> `微信登录` +4. `wechat_bind_phone` -> `绑定手机号` +5. `change_phone` -> `更换手机号` +6. `captcha_required` -> `需要图形验证码` +7. `logout` -> `退出当前设备` +8. `logout_all` -> `退出全部设备` +9. `revoke_session` -> `移除登录设备` +10. `risk_block_phone` -> `手机号临时保护` +11. `risk_block_ip` -> `网络临时保护` +12. `risk_unblock_phone` -> `解除手机号保护` +13. `risk_unblock_ip` -> `解除网络保护` + +### 7.2 `ipMasked` 不入库 + +原始 IP 继续入库,但对外返回时必须脱敏: + +1. IPv4 -> `a.b.*.*` +2. IPv6 -> 保留前两段,其余隐藏 + +## 8. 唯一约束与索引 + +### 8.1 必须具备的唯一约束 + +1. `audit_log_id` 主键唯一 + +### 8.2 必须具备的查询索引 + +1. `(user_id, created_at DESC)` + 作用:支撑 `/api/auth/audit-logs` +2. `(user_id, event_type, created_at DESC)` + 作用:后续按事件分类筛选或管理后台排查 +3. `(created_at DESC)` + 作用:后续归档与清理窗口扫描 + +## 9. 写入规则 + +### 9.1 登录相关 + +必须写入: + +1. 密码登录成功 -> `password_login` +2. 手机号登录成功 -> `phone_login` +3. 微信登录成功 -> `wechat_login` +4. 微信绑定手机号成功 -> `wechat_bind_phone` + +### 9.2 手机号变更相关 + +必须写入: + +1. 更换手机号成功 -> `change_phone` +2. 图形验证码被触发或校验失败 -> `captcha_required` + +说明: + +1. `captcha_required` 当前不是“成功动作”,而是一个安全门槛触发事件。 +2. 继续沿用现有 Node 语义,不再拆成 `captcha_challenge_issued` 和 `captcha_verify_failed` 两类。 + +### 9.3 会话相关 + +必须写入: + +1. 当前设备退出 -> `logout` +2. 退出全部设备 -> `logout_all` +3. 移除指定远端设备 -> `revoke_session` + +### 9.4 风控相关 + +必须写入: + +1. 手机号被封禁 -> `risk_block_phone` +2. IP 被封禁 -> `risk_block_ip` +3. 手机号封禁被解除 -> `risk_unblock_phone` +4. IP 封禁被解除 -> `risk_unblock_ip` + +## 10. `meta_json` 约定 + +`meta_json` 当前只放扩展上下文,不放主字段副本。 + +### 10.1 推荐写入内容 + +1. `revoke_session` + - `sessionId` + - `targetIp` + - `targetUserAgent` +2. `risk_block_phone` / `risk_block_ip` + - `scopeKey` + - `expiresAt` +3. `captcha_required` + - `scene` + - `phoneNumberMasked`(如需要) + +### 10.2 禁止写入内容 + +1. 原始 refresh token +2. 密码明文 +3. 短信验证码明文 +4. 微信 access token + +## 11. 读取规则 + +### 11.1 `/api/auth/audit-logs` + +当前阶段固定返回: + +1. 最近 20 条 +2. 按 `created_at DESC` +3. 只返回当前用户自己的审计记录 + +### 11.2 DTO 转换规则 + +对外 DTO `AuthAuditLogEntry` 固定为: + +1. `id` +2. `eventType` +3. `title` +4. `detail` +5. `ipMasked` +6. `userAgent` +7. `createdAt` + +其中: + +1. `id <- audit_log_id` +2. `title <- event_type` 派生 +3. `ipMasked <- ip` 脱敏后派生 + +## 12. 与当前 Node `auth_audit_logs` 的映射关系 + +| Node 列 | 新字段 | 迁移规则 | +| --- | --- | --- | +| `id` | `audit_log_id` | 原样迁移。 | +| `user_id` | `user_id` | 原样迁移。 | +| `event_type` | `event_type` | 原样迁移。 | +| `detail` | `detail` | 原样迁移。 | +| `ip` | `ip` | 原样迁移。 | +| `user_agent` | `user_agent` | 原样迁移。 | +| `meta_json` | `meta_json` | 原样迁移。 | +| `created_at` | `created_at` | 原样迁移。 | + +## 13. reducer / service 落地约束 + +### 13.1 `module-auth` reducer 层 + +必须至少具备: + +1. `append_auth_audit_log` + +说明: + +1. 审计表是典型追加型事实表,不需要复杂更新 reducer。 +2. 后续若做归档清理,再单独新增 maintenance reducer 或离线清理任务。 + +### 13.2 Axum 应用层 + +固定负责: + +1. 决定在哪个业务动作成功或触发门槛时写审计 +2. 组织 `detail` +3. 组织 `meta_json` +4. 读取时把 `event_type` 转为 `title` + +## 14. 不允许的设计漂移 + +后续实现时禁止出现以下情况: + +1. 把 `title` 直接入库,导致显示文案和事件真相耦合 +2. 为了省事复用 `auth_audit_log` 做当前风险态判断 +3. 为了省事复用 `auth_audit_log` 做短信频控统计 +4. 在审计表中记录原始 refresh token、验证码或密码明文 +5. 更新已有审计记录,而不是追加新事实 + +## 15. 本任务完成定义 + +当以下条件满足时,`设计 auth_audit_log` 视为完成: + +1. 审计表的职责边界已和会话表、风控表切开。 +2. 事件类型、字段、索引与读取 DTO 派生规则已明确。 +3. `/api/auth/audit-logs` 所需的查询语义已经固定。 +4. 后续可以直接据此编码 reducer、view 与 Axum 读取逻辑。 + +## 16. 依据文件 + +1. `server-node/src/auth/authService.ts` +2. `server-node/src/repositories/authAuditLogRepository.ts` +3. `server-node/src/routes/authRoutes.ts` +4. `server-node/src/db/migrations.ts` +5. `packages/shared/src/contracts/auth.ts` +6. `docs/prd/MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.md` +7. `docs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md` +8. `docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md` +9. `docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md` diff --git a/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md b/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md index 6f393917..d9c1316b 100644 --- a/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md +++ b/docs/technical/SPACETIMEDB_AXUM_OSS_BACKEND_REWRITE_DESIGN_2026-04-20.md @@ -318,6 +318,10 @@ server-rs/ - [SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md) +`auth_audit_log` 的事件范围、追加写规则与 DTO 派生约束,见: + +- [SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md](./SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md) + ### B. 运行时主状态表 - `runtime_snapshot` diff --git a/server-rs/packages/module-auth/README.md b/server-rs/packages/module-auth/README.md index e387af68..195b4414 100644 --- a/server-rs/packages/module-auth/README.md +++ b/server-rs/packages/module-auth/README.md @@ -27,6 +27,7 @@ 1. [../../../docs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.md) 2. [../../../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.md) 3. [../../../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md) +4. [../../../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_AUTH_AUDIT_LOG_TABLE_DESIGN_2026-04-21.md) ## 3. 边界约束