nagisa-kunhah commented on issue #1503:
URL: https://github.com/apache/dubbo-admin/issues/1503#issuecomment-5308455345

   # 引入通用 OAuth Provider 登录能力
   
   ## 变更概述
   
   为 Dubbo Admin 引入通用 OAuth Provider 登录能力,使 Admin 控制台可以接入外部身份系统、识别真实用户,并为后续 
Agent 用户身份识别、审计和权限控制打基础。GitHub OAuth App 作为第一个 Provider 实现,后续可增加标准 OIDC 
Provider。
   
   第一阶段聚焦 Admin Web 控制台登录和 AI 用户身份认证:不同 OAuth Provider 
负责完成各自的授权流程并解析外部用户身份,随后统一映射为 Principal 并建立 Admin session。GitHub Provider 使用 
GitHub access token 调用 `/user` 获取身份;后续标准 OIDC Provider 使用 ID Token 验签和 
claims/UserInfo 获取身份。Admin Web API 继续通过 session 识别登录态;浏览器需要调用独立 AI 服务时,Admin 
基于当前 session 签发短期 Access JWT,供 AI 服务独立校验。现有用户名密码登录继续保留,用于本地开发、兼容部署和紧急访问。
   
   首个 Provider 实现采用 GitHub OAuth App,其登录流程如下:
   
   ```mermaid
   sequenceDiagram
       autonumber
       actor User as 用户浏览器
       participant Admin as Dubbo Admin
       participant GitHub as GitHub OAuth
       participant API as GitHub API
       participant AI as AI 服务
   
       User->>Admin: GET /api/v1/auth/github/login
       Admin->>Admin: 生成 state 和 PKCE verifier<br/>写入 Admin session
       Admin-->>User: 302 重定向到 GitHub authorization endpoint
       User->>GitHub: 登录并授权
       GitHub-->>User: 回调 callback,携带 code 和 state
       User->>Admin: GET /api/v1/auth/github/callback
       Admin->>Admin: 校验 state
       Admin->>GitHub: 使用 code 和 PKCE verifier 换取 token
       GitHub-->>Admin: 返回 GitHub access token
       Admin->>API: GET /user,必要时 GET /user/emails
       API-->>Admin: 返回 GitHub 用户信息
       Admin->>Admin: 映射 Principal 并写入 Admin session
       Admin-->>User: 设置 session cookie 并重定向回 Admin UI
   
       Note over User,Admin: 后续 Admin API 继续使用 session cookie
       User->>Admin: POST /api/v1/auth/token<br/>携带 session cookie
       Admin-->>User: 返回短期 AI Access JWT
       User->>AI: Authorization: Bearer Access JWT
       AI->>AI: 使用 Admin JWKS 校验 JWT
   ```
   
   ## 背景
   
   当前 Dubbo Admin 的认证方式是配置文件中的固定用户名密码。后端登录成功后将 `user` 写入 Gin session,后续接口通过 
session 判断是否登录。
   
   这种方式实现简单,但在生产环境有明显限制:
   
   - 无法接入公司统一登录系统。
   - 只有一个静态账号,不能识别真实用户。
   - 不利于后续审计、权限控制和 Agent 代表用户访问。
   - 密码由 Dubbo Admin 自己维护,不符合企业 SSO 场景。
   
   Issue `apache/dubbo-admin#1503` 提到 OAuth2 覆盖 Admin 接入和 Agent 用户身份标识。本变更建立 
`OAuth Provider -> Principal -> Admin session` 的统一登录链路,第一阶段使用 GitHub OAuth App 
验证该框架,后续标准 OIDC Provider 复用相同的 Principal、session 和 AI Access JWT 链路。
   
   ## 目标
   
   - 建立通用 OAuth Provider 登录框架,并支持 Admin Web 控制台通过 GitHub OAuth App 登录。
   - 为后续标准 OIDC Provider 预留扩展能力,不改变统一 Principal 和登录后链路。
   - 保留现有用户名密码登录方式。
   - 将认证后的用户身份归一化为统一 Principal 模型。
   - 登录方式统一只创建 Principal 和 Admin session;Admin API 继续使用 session cookie 鉴权。
   - 提供基于当前 Admin session 获取短期 Access JWT 的接口,AI 服务使用该 JWT 独立校验用户身份。
   - 前端不保存 OAuth Provider 的 `access_token`、`id_token` 或 `refresh_token`;Admin 
签发的 Access JWT 仅保存在前端运行时内存。
   - 提供 fake GitHub OAuth server,用于本地开发和 e2e 测试。
   - 为后续 RBAC、审计和更细粒度的 Agent 用户授权留出扩展点。
   
   ## 非目标
   
   - 第一阶段不维护 refresh token。
   - 第一阶段不将 Admin API 的既有 session 鉴权迁移为 Bearer JWT 鉴权。
   - 第一阶段不实现 token introspection、黑名单或 JWT 即时撤销。
   - 第一阶段不实现 RBAC 权限拦截。
   - 第一阶段只实现 GitHub OAuth App Provider,不在本阶段实现标准 OIDC Provider、其他 OAuth 
Provider 或 GitHub Enterprise Server;这些 Provider 与 GitHub 不互斥,可在后续基于同一框架增加。
   - 第一阶段不支持手动配置 GitHub authorization、token 和 API endpoint override。
   - 第一阶段不实现 GitHub 级别的单点退出或 token revoke。
   - 第一阶段不实现跨 Provider 账号绑定。
   
   ## 当前实现位置
   
   当前认证相关代码主要在:
   
   - `pkg/config/console/auth/config.go`:认证配置结构。
   - `pkg/console/router/router.go`:`/api/v1/auth/login` 和 
`/api/v1/auth/logout` 路由。
   - `pkg/console/handler/auth.go`:用户名密码登录和退出逻辑。
   - `pkg/console/component.go`:Gin session 初始化和登录态校验中间件。
   - `ui-vue3/src/Login.vue`:前端登录页。
   - `ui-vue3/src/api/service/login.ts`:前端登录 API 封装。
   - `ui-vue3/src/utils/AuthUtil.ts`:前端本地 `auth-state` 状态。
   
   当前后端中间件只跳过路径后缀为 `/login` 的请求,GitHub OAuth callback 接入后需要改为显式匿名路径白名单。
   
   ## 用户流程
   
   ### 用户名密码登录
   
   1. 用户打开 Admin UI。
   2. 前端提交用户名密码到 `POST /api/v1/auth/login`。
   3. 后端校验配置文件中的 `console.auth.user` 和 `console.auth.password`。
   4. 校验成功后写入 Principal session。
   5. 后续 Admin API 请求继续通过 session cookie 识别登录态。
   
   ### GitHub OAuth 登录
   
   1. 用户打开 Admin UI。
   2. 登录页展示 GitHub 登录入口。
   3. 用户点击 GitHub 登录,浏览器访问 `GET /api/v1/auth/github/login`。
   4. 后端生成 `state` 和 PKCE 参数,并暂存到 session。
   5. 后端重定向浏览器到 GitHub authorization endpoint。
   6. 用户在 GitHub 完成登录和授权。
   7. GitHub 回调 `GET 
/api/v1/auth/github/callback?code=<authorization-code>&state=<state>`。
   8. 后端校验 `state`,使用 `code` 和 PKCE verifier 换取 GitHub access token。
   9. 后端使用 GitHub access token 调用 `/user` 获取用户身份,必要时调用 `/user/emails` 获取邮箱。
   10. 后端创建统一 Principal 并写入 session。
   11. 后端重定向用户回 Admin UI。
   12. 前端调用 `GET /api/v1/auth/userinfo` 获取当前用户信息;该请求继续使用 Admin session。
   
   ### AI Access JWT 获取与使用
   
   1. password 和 OAuth Provider 登录均只创建 Principal 和 Admin session,不直接返回 Access 
JWT。
   2. 前端发起需要认证的 AI 请求前,检查运行时内存中的 Access JWT 的 `exp`。
   3. JWT 不存在、已过期或接近过期时,前端使用当前 Admin session 调用 `POST /api/v1/auth/token` 获取新的 
Access JWT。
   4. 多个并发请求同时需要新 JWT 时,前端必须合并续签,只允许一个 `/api/v1/auth/token` 请求,其余请求等待同一结果。
   5. 前端仅在运行时内存保存 JWT,并在调用 AI 服务时设置 `Authorization: Bearer <access-token>`;不使用 
localStorage、sessionStorage、普通 Cookie 或定时刷新任务。
   6. AI 服务返回表示 Access JWT 无效的 401 时,前端强制重新获取一次 JWT,并最多重试原请求一次;403 
不触发续签,且禁止无限重试。
   7. 页面刷新后内存 JWT 消失;下一次需要认证的 AI 请求仍可通过有效的 Admin session 按需获取 JWT。
   
   ## 配置变更
   
   建议扩展 `console.auth`:
   
   ```yaml
   console:
     auth:
       methods:
         - password
         - github
       user: admin
       password: dubbo@2025
       expirationTime: 3600
       sessionSecret: ${DUBBO_ADMIN_SESSION_SECRET}
       accessToken:
         issuer: dubbo-admin
         keyId: admin-key-1
         privateKey: ${DUBBO_ADMIN_ACCESS_TOKEN_PRIVATE_KEY}
         ttl: 1800
         audiences:
           - dubbo-admin-ai
       github:
         clientId: ${DUBBO_ADMIN_GITHUB_CLIENT_ID}
         clientSecret: ${DUBBO_ADMIN_GITHUB_CLIENT_SECRET}
         redirectUrl: http://localhost:8888/api/v1/auth/github/callback
         postLoginRedirectUrl: http://localhost:8881/admin/
         scopes:
           - read:user
           - user:email
   ```
   
   说明:
   
   - `methods` 控制启用的认证方式。
   - `methods` 中的 Provider 名称对应具体 Provider 实现;后续可增加 `oidc`,并允许与 `github` 同时启用。
   - `password` 登录沿用现有 `user` 和 `password`。
   - `github.clientId` 和 `github.clientSecret` 来自 GitHub OAuth App。
   - `redirectUrl` 是注册到 GitHub OAuth App 的 callback 地址。
   - `postLoginRedirectUrl` 是 callback 成功后回到前端的地址。
   - `sessionSecret` 替代当前硬编码的 `"secret"`。
   - `accessToken` 配置 Admin 基于 session 签发给 AI 服务的 Access JWT;`ttl` 默认 1800 秒,AI 
校验 `dubbo-admin-ai` audience。
   
   ## 协议库使用
   
   Dubbo Admin 不自行实现 OAuth2/OIDC 协议细节,只实现通用 OAuth Provider 与自身登录体系对接的 HTTP 接口和 
session 逻辑。
   
   OAuth Provider 统一负责:
   
   - 生成登录跳转地址。
   - 使用 callback 中的 authorization code 换取 Provider token。
   - 从 Provider token 解析并返回统一 Principal。
   
   不同 Provider 只在身份解析方式上存在差异,Principal、Admin session、`/api/v1/auth/token` 和 AI 
Access JWT 链路保持一致。
   
   OAuth2 授权码流程应使用 Go 官方维护的 `golang.org/x/oauth2`:
   
   - 使用 `oauth2.Config.AuthCodeURL` 生成 Provider 登录跳转地址。
   - 使用 `oauth2.Config.Exchange` 将 authorization code 换取 token。
   - 使用 `oauth2.Config` 管理 `clientId`、`clientSecret`、`redirectUrl` 和 `scopes`。
   - 使用 `golang.org/x/oauth2/github` 提供的 GitHub endpoint 配置。
   
   GitHub 用户身份通过官方 REST API 获取:
   
   - 使用 access token 调用 `GET https://api.github.com/user`。
   - 需要获取非公开邮箱时调用 `GET https://api.github.com/user/emails`。
   - 使用稳定的 GitHub 数值用户 ID 作为身份主键,不使用可能变化的 `login` 作为 Subject。
   
   后续标准 OIDC Provider 使用成熟 OIDC 库实现:
   
   - 通过 `issuer` 执行 OIDC discovery。
   - 校验 ID Token 的 signature、issuer、audience、expiration 和 nonce。
   - 从 ID Token claims 获取用户身份,必要时调用 UserInfo endpoint。
   - 将 `sub`、用户名、邮箱、groups 和 roles 映射为同一 Principal 模型。
   
   Dubbo Admin 自己负责:
   
   - 暴露 `/api/v1/auth/github/login` 和 `/api/v1/auth/github/callback`。
   - 生成、保存和校验 `state`、PKCE 参数。
   - 使用 GitHub access token 获取用户信息并映射为 Principal。
   - GitHub access token 仅在后端 callback 流程中使用,不写入 session,也不返回前端。
   - Provider 实现只返回统一 Principal,不将 GitHub access token、OIDC ID Token 或 refresh 
token 传递给公共登录链路。
   - 创建和清理 Dubbo Admin 本地 session。
   - 基于已认证 session 签发 AI Access JWT,并发布用于验签的 JWKS 公钥。
   - 将用户重定向回 Admin UI。
   
   ## 新增或调整的接口
   
   新增:
   
   - `GET /api/v1/auth/github/login`:发起 GitHub OAuth authorization code flow。
   - `GET /api/v1/auth/github/callback`:处理 GitHub callback 并创建本地 session。
   - `POST /api/v1/auth/token`:使用当前 Admin session 获取供 AI 服务使用的短期 Access JWT;不接受 
refresh token。
   - `GET /api/v1/auth/jwks`:发布 AI Access JWT 的验签公钥。
   - `GET /api/v1/auth/userinfo`:返回当前登录用户 Principal。
   
   GitHub 使用上述 provider-specific 登录和 callback 路由;后续 OIDC Provider 
可增加对应登录路由,但必须复用相同的 Principal、session、userinfo 和 AI token 接口。
   
   保留:
   
   - `POST /api/v1/auth/login`:用户名密码登录。
   - `POST /api/v1/auth/logout`:清理 Dubbo Admin 本地 session;前端同时清除运行时内存中的 Access 
JWT。
   
   调整:
   
   - auth middleware 使用显式 allowlist。
   - 允许匿名访问登录入口、GitHub OAuth callback 和 health check。
   - `/api/v1` 下业务接口默认要求有效的 Admin session;Access JWT 获取接口要求有效的 Admin session,AI 
服务的接口由其自身验证 Access JWT。
   
   ## Principal 模型
   
   统一身份模型建议为:
   
   ```go
   type Principal struct {
       Subject  string
       Username string
       Email    string
       Groups   []string
       Roles    []string
       AuthType string
       Provider string
   }
   ```
   
   GitHub OAuth 登录时:
   
   - `Subject` 使用稳定的 GitHub 数值用户 ID,格式为 `github:<id>`。
   - `Username` 来自 GitHub `login`。
   - `Email` 来自 `/user` 或 `/user/emails`,不存在时允许为空。
   - `Groups` 和 `Roles` 第一阶段为空,不参与权限拦截。
   - `AuthType` 为 `oauth`,`Provider` 为 `github`。
   
   标准 OIDC 登录时:
   
   - `Subject` 来自 ID Token 的 `sub`,并以 Provider 标识作为命名空间,避免与其他 Provider 冲突。
   - `Username`、`Email`、`Groups` 和 `Roles` 来自 ID Token claims 或 UserInfo。
   - `AuthType` 为 `oidc`,`Provider` 使用对应 OIDC Provider 标识。
   
   用户名密码登录时:
   
   - `Username` 来自表单中的用户名。
   - `AuthType` 为 `password`。
   
   ## Fake GitHub OAuth Server
   
   为了本地开发和 e2e 测试,需要提供轻量 fake GitHub OAuth server。它只模拟 GitHub OAuth 和用户 API 
的协议边界。
   
   测试通过依赖注入替换 GitHub OAuth 和 API endpoint,不对生产配置开放 endpoint override。
   
   建议位置:
   
   - `test/fakegithub/`:fake GitHub OAuth server 代码。
   - `app/dubbo-admin/dubbo-admin-github-oauth-local.yaml`:本地 GitHub OAuth 配置。
   
   最小 endpoint:
   
   - `GET /login/oauth/authorize`
   - `POST /login/oauth/access_token`
   - `GET /user`
   - `GET /user/emails`
   
   测试用户示例:
   
   ```json
   {
     "id": 123456,
     "login": "zhangsan",
     "email": "[email protected]",
     "name": "Zhang San"
   }
   ```
   
   fake server 至少支持:
   
   - 正常登录。
   - 无效 authorization code。
   - state 不匹配。
   - GitHub 用户 ID 缺失或用户信息格式错误。
   
   ## 测试要求
   
   后端测试应覆盖:
   
   - 未登录访问业务接口返回 401。
   - 用户名密码登录仍然可用。
   - GitHub login 正确重定向到 GitHub authorization endpoint。
   - GitHub callback 拒绝错误 state。
   - GitHub callback 拒绝错误 code。
   - GitHub callback 成功后写入 Principal session。
   - Provider 返回的身份统一映射为 Principal,后续 session 和 AI Access JWT 行为与 Provider 类型无关。
   - 登录后访问业务接口成功。
   - 未登录时无法通过 `/api/v1/auth/token` 获取 Access JWT。
   - 使用当前 Principal 签发的 Access JWT 可通过 AI 服务验签;过期或无效 JWT 被 AI 服务拒绝。
   - logout 后 session 失效,前端内存中的 Access JWT 被清除;已签发 JWT 在过期前不支持即时撤销。
   
   e2e 测试应覆盖:
   
   - 用户从登录页点击 GitHub 登录。
   - fake GitHub OAuth server 自动完成授权回调。
   - 前端显示 fake 用户身份。
   - 登录后业务 API 请求成功。
   - 首次 AI 请求按需获取 Access JWT;并发 AI 请求只触发一次 token 获取。
   - AI 返回 Access JWT 无效的 401 时只重试一次,403 不触发续签。
   - 页面刷新后不恢复 JWT 到浏览器持久化存储,而是在下一次 AI 请求时基于 session 重新获取。
   - 退出后再次访问业务页面会回到登录页。
   
   ## 安全要求
   
   - 必须校验 `state`。
   - 推荐使用 authorization code flow + PKCE。
   - `clientSecret` 只能保存在后端配置中。
   - GitHub access token 只能用于后端调用 GitHub API,不得写入 session、前端 localStorage 或普通 
cookie。
   - 标准 OIDC Provider 必须校验 ID Token 的 signature、issuer、audience、expiration 和 
nonce;ID Token 和 refresh token 不得暴露给前端。
   - Admin 签发的 AI Access JWT 只可保存在前端运行时内存,默认有效期为 30 分钟;不使用 refresh token,JWT 
在过期前不支持即时撤销,因此泄露后仍可能被重放至 `exp`。
   - Admin session cookie 应设置 HttpOnly 和合适的 SameSite;HTTPS 场景必须设置 
Secure。Session 只负责浏览器与 Admin 的登录态,不作为 AI 服务认证凭据。
   - GitHub OAuth 开启时,生产环境不得使用默认 session secret。
   
   ## 兼容性
   
   现有用户名密码登录继续可用,避免破坏已有部署。默认配置可以继续只启用 password。启用 GitHub OAuth 时,通过 `methods` 
显式打开。第一阶段允许同时启用 password 和 github;后续增加标准 OIDC Provider 后,github、oidc 和 password 
可按部署需要组合启用,彼此不互斥。
   
   前端现有 `auth-state` 只应作为 UI 状态缓存,不能作为后端认证凭据。前端通过 `/api/v1/auth/userinfo` 
同步当前真实登录态,并仅在运行时内存维护 AI Access JWT。
   
   ## 已确认决策
   
   - 认证设计采用通用 OAuth Provider 框架,GitHub.com OAuth App 是第一个 Provider 实现,后续可增加标准 
OIDC Provider。
   - GitHub OAuth 与标准 OIDC 不互斥,两者统一映射 Principal 并复用 Admin 
session、`/api/v1/auth/token` 和 AI Access JWT 链路。
   - 第一阶段保留用户名密码登录。
   - 第一阶段允许 password 和 github 同时启用。
   - OAuth2 授权码流程使用 `golang.org/x/oauth2`,不手写协议细节。
   - GitHub 用户身份通过 `/user` 和必要时的 `/user/emails` 获取,稳定数值 ID 映射为 Principal 
Subject。
   - Admin session cookie 负责 Web 登录态;Admin API 保持现有 session 鉴权。
   - Access JWT 仅用于浏览器调用 AI 服务,由 `/api/v1/auth/token` 基于有效 session 按需签发,默认 TTL 
为 30 分钟。
   - Access JWT 不持久化,不使用 refresh token 或定时刷新;并发续签合并,401 最多续签并重试一次,403 不续签。
   - logout 清除 Admin session 和前端内存 JWT;第一阶段不撤销已签发的 JWT。
   
   ## 开放问题
   
   - `sessionSecret` 的默认值和生产环境校验策略如何定义。
   - GitHub OAuth scopes 是否默认包含 `user:email`,还是仅在部署需要邮箱时启用。
   - `/admin` 静态资源是否需要登录态保护,还是只保护 `/api/v1` 业务接口。
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to