easyai-ai-gateway/docs/oidc-jit-provisioning.md
chengcheng a81a7b5200 fix: 修复 OIDC 用户预配与跨标签页登录态
增加受控 JIT 本地用户投影,并使用 HttpOnly Cookie 在新标签页恢复认证中心登录态。补充错误语义、安全校验、自动化测试与回滚配置。
2026-07-13 17:07:52 +08:00

64 lines
4.9 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.

# Auth Center OIDC 用户受控 JIT 预配
## 适用边界
Gateway 只在 OIDC Token 已通过签名、Issuer、Audience、有效期、`tid`、Scope 和应用角色校验后执行 JIT。认证中心的稳定 `sub` 是外部用户标识Gateway 不读取、不保存或公开 Keycloak 内部 ID也不向 Token 增加 `gatewayUserId`
本次保持单 Gateway 租户:部署方用 `OIDC_GATEWAY_TENANT_KEY` 把 Auth Center 的已验证 `tid` 显式绑定到一个已存在、启用且配置了启用中默认用户组的 Gateway 租户。Token 不能创建租户。
## 配置
```dotenv
OIDC_ENABLED=true
OIDC_JIT_PROVISIONING_ENABLED=true
OIDC_GATEWAY_TENANT_KEY=default
OIDC_BROWSER_SESSION_ENABLED=true
OIDC_SESSION_COOKIE_SECURE=false # 本地 HTTP生产必须为 true
```
- `OIDC_JIT_PROVISIONING_ENABLED` 默认 `false`。关闭时停止创建新用户,但已有 `source=oidc + external_user_id=sub` 映射仍会解析和同步登录时间。
- JIT 开启时 `OIDC_GATEWAY_TENANT_KEY` 必填,缺失会使 Gateway 启动失败。
- 本地与 Staging 验收环境应在各自 Git 忽略或 Secret 管理的环境文件中显式开启;生产启用需独立变更审批。
## Web Console 浏览器会话
- OIDC 回调仍使用 Authorization Code + PKCE 从 Auth Center 获取 Access Token但 Token 只在回调函数内存中短暂存在。
- Web 随即调用 `POST /api/v1/auth/oidc/session`Gateway 再次完成全部 OIDC 校验后,将原 Auth Center Access Token 写入 `HttpOnly + SameSite=Strict` Cookie不签发第二枚 Gateway JWT。
- Cookie 有效期不超过 Access Token 的 `exp`,所有 Cookie 请求仍经过 JWKS、Issuer、Audience、`tid`、Scope、角色及可选 Introspection 校验。
- 建立成功后清除旧的 OIDC `sessionStorage` 和可能冲突的 `localStorage` Token新标签页通过 Cookie 调用 `/api/v1/me` 自动恢复同一登录态。
- 所有 Web API 请求使用 `credentials: include`。Cookie 鉴权的 POST、PUT、PATCH、DELETE 必须携带 `CORS_ALLOWED_ORIGIN` 白名单中的 Origin否则返回结构化 403。
- Staging、生产及其他非本地环境启动时强制 `OIDC_SESSION_COOKIE_SECURE=true`,并拒绝带 `*` 的凭据型 CORS 配置。本地 HTTP 开发和自动化测试可以显式设为 `false`
- `DELETE /api/v1/auth/oidc/session` 使 Cookie 立即过期,然后前端进入 Auth Center OIDC 登出。浏览器会话关闭时不会创建或撤销 Gateway API Key。
- 回滚时同时设置后端 `OIDC_BROWSER_SESSION_ENABLED=false` 和 Web 构建参数 `VITE_OIDC_BROWSER_SESSION_ENABLED=false`,恢复旧的标签页级 `sessionStorage` 行为。
## 数据和事务语义
- 查找键固定为 `source=oidc + external_user_id=sub``user_key` 由规范化 Issuer 和 `sub` 做 SHA-256 派生,不按邮箱、手机号、昵称匹配。
- 首次登录在一个事务中创建 `gateway_users` 投影、绑定租户默认用户组、初始化 `resource` 钱包并写入 `identity.oidc_user.provisioned` 审计事件。
- 重复或并发登录依赖现有唯一约束和冲突处理返回同一个本地用户,不重复创建钱包或首次预配审计。
- 后续登录只同步 Token 用户名、应用角色、`last_login_at`、`synced_at` 和 `source_updated_at`;不覆盖人工资料或用户组,不重新启用已禁用/删除账号。
- JIT 不生成 API Key 或任何 secret。用户仅在主动创建 API Key 或进入需要 Key 的工作流时触发生命周期操作。
## 对外错误
所有错误沿用 `ErrorEnvelope`
| HTTP | code | 含义 |
| --- | --- | --- |
| 403 | `GATEWAY_USER_NOT_PROVISIONED` | JIT 关闭且没有已有本地映射 |
| 403 | `GATEWAY_USER_DISABLED` | 本地用户已禁用或删除 |
| 503 | `GATEWAY_TENANT_UNAVAILABLE` | 配置租户或用户组不存在/未启用 |
| 503 | `GATEWAY_USER_PROVISIONING_FAILED` | 事务或存储故障;响应不暴露内部错误 |
| 404 | `OIDC_BROWSER_SESSION_DISABLED` | Gateway 浏览器会话功能未启用 |
| 401 | `OIDC_SESSION_INVALID` | 不是有效的 Auth Center OIDC Access Token 或已过期 |
| 400 | `OIDC_SESSION_TOKEN_TOO_LARGE` | Access Token 超过安全 Cookie 大小限制 |
| 403 | `OIDC_SESSION_CSRF_REJECTED` | Cookie 写请求缺少可信 Origin |
`ErrLocalUserRequired` 仅作为防御性内部错误保留,对外统一转换为结构化 403。
## 验收与回滚
自动化门禁通过后,依次验证本地真实 OIDC 和 `auth.51easyai.com` Staging 专用测试租户。证据应包含脱敏 Claims、网络截图、本地 Gateway 用户记录、Trace ID、Gateway/Auth Center 审计 ID及 200/401/403/503 负向证据;不得保存 Token、授权码、密码或 Secret。
JIT 回滚时关闭 `OIDC_JIT_PROVISIONING_ENABLED` 并回退 Gateway 镜像。浏览器 Cookie 会话可通过后端和 Web 两侧的独立开关关闭。已经创建的 `source=oidc` 测试投影保留为惰性数据,不自动删除、迁移或合并。