统一 IdP 抽象(OIDC + OAuth2)
把 Inbound OIDC 的
tenant_oidc_providers扩展成「外部身份提供方」统一抽象,让租户在 UI 上自助配置任意登录源——既支持标准 OIDC IdP(Keycloak/Azure/Google),也支持 OAuth2 提供方(Gitee/GitHub/微信/飞书等非标准 OIDC),并按租户停用。 定稿 2026-07-10。前置:Inbound OIDC(docs/TENANT_PHASE5_INBOUND_OIDC.md)、Gitee 硬编码(commit1a058c7)。
0. 为什么
当前 auth 有两套登录入口:
- 硬编码 handler:GitHub、Gitee(每家一个 handler + 全局 client_id/secret,所有租户共享,租户无法停用/自定义)。
- 配置化 IdP:Inbound OIDC(
tenant_oidc_providers,每租户配企业 IdP,可停用)——但只支持标准 OIDC(依赖 discovery + id_token + 标准 userinfo),接不上 Gitee/微信等 OAuth2 平台。
目标是统一成一套配置:租户管理员在 UI 配一个 IdP(选类型 OIDC/OAuth2),填对应参数,就能接入任意登录源,并能停用。这是 IDaaS 的核心能力(Auth0/Casbin 的 social connections 都这么设计)。
1. 数据模型扩展(tenant_oidc_providers 加字段)
ALTER TABLE tenant_oidc_providers ADD COLUMN provider_type VARCHAR(10) DEFAULT 'oidc'; -- 'oidc' | 'oauth2'
-- OAuth2 手填 endpoints(OIDC 用 discovery,这几列留空)
ALTER TABLE tenant_oidc_providers ADD COLUMN authorize_url TEXT;
ALTER TABLE tenant_oidc_providers ADD COLUMN token_url TEXT;
ALTER TABLE tenant_oidc_providers ADD COLUMN userinfo_url TEXT;
-- OAuth2 userinfo 字段映射(OIDC 用标准 sub/email,这几列留空走默认)
ALTER TABLE tenant_oidc_providers ADD COLUMN field_id VARCHAR(50) DEFAULT 'id';
ALTER TABLE tenant_oidc_providers ADD COLUMN field_username VARCHAR(50) DEFAULT 'login';
ALTER TABLE tenant_oidc_providers ADD COLUMN field_email VARCHAR(50) DEFAULT 'email';
ALTER TABLE tenant_oidc_providers ADD COLUMN field_name VARCHAR(50) DEFAULT 'name';
ALTER TABLE tenant_oidc_providers ADD COLUMN field_avatar VARCHAR(50) DEFAULT 'avatar_url';
-- OAuth2 token 传递与编码
ALTER TABLE tenant_oidc_providers ADD COLUMN userinfo_token_in VARCHAR(10) DEFAULT 'query'; -- 'query'(?access_token=) | 'header'(Authorization: Bearer)
ALTER TABLE tenant_oidc_providers ADD COLUMN token_content_type VARCHAR(10) DEFAULT 'form'; -- 'form'(x-www-form-urlencoded) | 'json'
现有 IdP 配置迁移:provider_type='oidc'(默认),新字段空走 OIDC discovery,零破坏。
2. RP 逻辑泛化(src/sso.rs)
start(GET /api/auth/sso/:provider_id/start)
按 provider_type 分支:
- oidc:
discover(issuer)拿 authorization_endpoint + 生成 PKCE(code_challenge)+ state 存 Redis → 302。(现有逻辑) - oauth2:用
authorize_url(手填)+ state 存 Redis(不生成 PKCE,多数 OAuth2 如 Gitee/GitHub 不要求;若某些平台要,可后续加use_pkce开关)→ 302。
callback(GET /api/auth/sso/callback?code=&state=)
- 换 token:
- oidc:
POST discovery.token_endpoint+code_verifier(PKCE)→{access_token, id_token}。 - oauth2:
POST token_url,body 按token_content_type(form/json),含grant_type=authorization_code, code, client_id, redirect_uri, client_secret(无 code_verifier)→{access_token}。
- oidc:
- userinfo:
- oidc:
GET discovery.userinfo_endpoint+Authorization: Bearer→ 标准{sub, email, name}。 - oauth2:
GET userinfo_url,token 按userinfo_token_in(query?access_token=或 header)→ 按field_*映射取{id, username, email, name, avatar}。
- oidc:
- 用户映射(统一):
oauth_provider = format!("idp:{}", provider_id),oauth_id = mapped_id。
公开端点(GET /api/auth/sso/providers?tenant_id=)
返 [{id, name, provider_type}](加 provider_type 供登录页按类型显示图标/文案;不返敏感)。
3. 字段映射示例(覆盖主流 OAuth2 平台)
| 平台 | authorize_url | token_url | userinfo_url | field_id | field_username | userinfo_token_in | token_content_type |
|---|---|---|---|---|---|---|---|
| Gitee | https://gitee.com/oauth/authorize | https://gitee.com/oauth/token | https://gitee.com/api/v5/user | id | login | query | form |
| GitHub | https://github.com/login/oauth/authorize | https://github.com/login/oauth/access_token | https://api.github.com/user | id | login | header | json |
| 通用 OAuth2 | 各自填 | 各自填 | 各自填 | 配置 | 配置 | 配置 | 配置 |
租户管理员在 UI 填这些 → 接入对应平台。
4. UI(admin-ui/src/sso_providers.vue)
- 表单加「类型」选择:OIDC / OAuth2。
- OIDC 显示:issuer(discovery 用)。
- OAuth2 显示:authorize_url / token_url / userinfo_url + 字段映射(field_id/username/email/name/avatar)+ userinfo_token_in(query/header)+ token_content_type(form/json)+ scope。
- 通用:name / client_id / client_secret / is_active(停用开关)。
- 预置模板(可选 UX 加分):点「Gitee 模板」自动填 Gitee 的 URL/字段映射,租户只补 client_id/secret。
5. 硬编码 GitHub/Gitee 已退役(2026-07)
- 硬编码
github_oauth_*/gitee_oauth_*handler 与.env的GITHUB_*/GITEE_*配置已移除。 - 全部统一到配置化 IdP:社交登录(GitHub/Gitee)和企业 SSO(OIDC)都走
tenant_oidc_providers表 +/api/auth/sso/*路径。 - 功能平移:db.rs 启动迁移在 default 租户下自动创建 GitHub OAuth2 IdP(读
.env的GITHUB_CLIENT_ID/SECRET,幂等判重);Gitee 早已预置(id=1)。 - 历史
oauth_provider值:硬编码'github'/'gitee'(遗留账号)vs 配置'idp:{id}'(新登录)。两套不冲突,老账号原值不变。
6. 工作量(1 人估)
| 子项 | 工作量 |
|---|---|
| 表扩展 + 迁移 + 模型字段 | ~1 天 |
| RP 逻辑泛化(start/callback 按 type 分支 + 字段映射 + token 传递) | ~3 天 |
| 公开端点 + UI(sso_providers.vue type 选择 + 条件字段 + 模板) | ~2 天 |
| 测试(oidc 路径不回归 + oauth2 用 Gitee 实测) | ~1 天 |
| 合计 | ~1 周 |
7. 边界(本阶段不做)
- ❌ 硬编码 GitHub/Gitee 迁移到 IdP 配置(保留兼容,迁移影响现有用户 oauth_provider 值)。
- ❌ 微信/飞书自有协议适配(它们连标准 OAuth2 都不算,有 openid/app_id 等特有字段,要再单独适配;本阶段只支持「标准 OAuth2 + 字段映射」能覆盖的平台如 Gitee/GitHub)。
- ❌ SAML(仍等大客户要再做)。
- ❌ 租户级硬编码停用(硬编码是平台预置全局,租户不能停;租户只管自己配的 IdP)。