ClouisleClouisle

SSO API

单点登录流程、连接解绑与管理员供应商配置

SSO API 覆盖三件事:登录页的供应商发现、基于浏览器重定向的单点登录流程(OAuth2 / OIDC、SAML2、CAS),以及用户解绑自己的 SSO 身份。供应商的增删改查属于管理端,挂在 /api/v1/admin/sso。

前置条件与认证

GET /api/v1/sso/providers、GET /api/v1/sso/login/{provider_name}、GET /api/v1/sso/callback/{provider_name} 是公开端点,不需要认证,也返回重定向或 JSON 而非统一信封。

DELETE /api/v1/sso/connections/{connection_id} 需要登录用户(只能解绑自己的连接)。

管理端点需要权限码:GET /api/v1/admin/sso/providers 需要 admin:sso:read;创建、更新、删除、测试供应商以及解绑任意用户的连接需要 admin:sso:update。

供应商配置(config、attribute_mapping、allow_signup、require_approval、default_role_id)见 SSO 配置。

login 与 callback 返回 HTTP 307 重定向,不是 {code, data, msg} 信封。前端在 /sso-callback 路由上接收 token 或 error 查询参数。若站点设置 sso_enabled 为 false,GET /sso/providers 返回空数组 [],即使已配置供应商也不会暴露。

端点总览

方法路径用途认证
GET/api/v1/sso/providers列出已启用的供应商(登录页使用)公开
GET/api/v1/sso/login/{provider_name}发起 SSO 登录,重定向到身份提供商公开
GET/api/v1/sso/callback/{provider_name}身份提供商回调,处理登录并跳回前端公开
DELETE/api/v1/sso/connections/{connection_id}解绑当前用户的某个 SSO 连接登录用户
GET/api/v1/admin/sso/providers列出全部供应商(含禁用)admin:sso:read
POST/api/v1/admin/sso/providers创建供应商admin:sso:update
PUT/api/v1/admin/sso/providers/{provider_id}更新供应商admin:sso:update
DELETE/api/v1/admin/sso/providers/{provider_id}删除供应商admin:sso:update
POST/api/v1/admin/sso/providers/{provider_id}/test测试供应商配置能否生成授权地址admin:sso:update
DELETE/api/v1/admin/sso/connections/{connection_id}解绑任意用户的 SSO 连接admin:sso:update

1. 列出已启用的供应商

GET /api/v1/sso/providers

登录页用它渲染 SSO 按钮。只返回 is_enabled=true 的供应商,且不包含任何敏感配置。

成功响应(200 OK):

{
  "code": 0,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "github",
      "display_name": "GitHub",
      "icon_url": "https://cdn.example.com/github.svg",
      "button_text": "Continue with GitHub",
      "protocol": "oauth2"
    }
  ],
  "msg": "success"
}
字段类型说明
idstring (UUID)供应商 ID
namestring供应商唯一标识(登录 URL 中使用)
display_namestring展示名称
icon_urlstring | null图标地址
button_textstring | null按钮文案
protocolstringoauth2、oidc、saml2 或 cas

2. 发起 SSO 登录

GET /api/v1/sso/login/{provider_name}

查询参数

参数类型必填说明
redirectstring否登录成功后要落到的同源相对路径(如 /dashboard)。非相对路径、含协议/主机、以 // 开头或含反斜杠的值一律被忽略并回退到 /dashboard

行为:

  1. 校验供应商存在且已启用,否则直接重定向到前端 {frontend}/sso-callback?error=sso_provider_not_found。
  2. 生成随机 state(即 session_id,secrets.token_urlsafe(32)),创建一条 10 分钟后过期的 SSO 会话,保存 PKCE code_verifier / nonce(OAuth2、OIDC)或 RelayState。
  3. 回调地址由后端来源拼接:{backend}/api/v1/sso/callback/{provider_name}。
  4. 返回 307 Temporary Redirect 到身份提供商的授权地址。

失败重定向:

{frontend}/sso-callback?error=<error_code>&redirect=<path>
error含义
sso_provider_not_found供应商不存在或未启用
sso_login_failed供应商初始化失败(协议不支持、配置错误等)

关于 state

对 OAuth2 / OIDC,state 就是服务端生成的 SSO 会话 ID,回调时用它取回 PKCE code_verifier / nonce;SAML2 通过 RelayState、CAS 通过 state 或 ticket 传递同一个会话 ID。因此回调链接中的 state 不是客户端自定义值,不要自行拼接或复用。


3. 身份提供商回调

GET /api/v1/sso/callback/{provider_name}

身份提供商在用户完成认证后把浏览器重定向到这里。参数随协议不同:

协议回调参数会话标识来源
oauth2 / oidccode、statestate
casticket(可带 state)state 或 ticket
saml2表单 POST 的 SAMLResponse、RelayStateRelayState

成功 → 浏览器重定向到:

{frontend}/sso-callback?token=<access_token>&redirect=<path>

token 是标准 JWT 访问令牌(有效期由站点设置 session_timeout_days 决定,默认 7 天)。若开启了 single_session,服务端会登记该会话使旧令牌失效。SSO 会话在成功后立即删除。

失败 → 重定向到 {frontend}/sso-callback?error=<error_code>&redirect=<path>。常见 error:

error含义
sso_provider_not_found供应商不存在
sso_session_expired会话缺失或已超过 10 分钟
sso_login_failed换取用户信息失败、provider_user_id 缺失
pending_approval新用户需要管理员审批才能登录
inactive账号已停用

账号被停用或待审批时,SSO 流程不签发令牌,只把 error 带回前端;请据此展示提示,而不是把用户当作已登录。


4. 解绑自己的 SSO 连接

DELETE /api/v1/sso/connections/{connection_id}

路径参数

参数类型必填说明
connection_idstring (UUID)是用户 SSO 连接 ID

只能操作属于当前用户的连接。若账号没有设置密码,且这是最后一条 SSO 连接,删除会被拒绝(否则账号将无法登录)。

当前用户的连接列表可从 GET /api/v1/users/me 的 sso_connections 字段读取,见 用户 API。

成功响应(200 OK):

{
  "code": 0,
  "data": null,
  "msg": "success"
}

错误:

HTTP错误码说明
4004000sso_connection_not_found:连接不存在或不属于当前用户
4001004cannot_disconnect_only_auth_method:无密码且这是唯一登录方式

5. 管理端:供应商配置

以下端点挂载在 /api/v1/admin/sso。

5.1 列出全部供应商

GET /api/v1/admin/sso/providers

需要 admin:sso:read。返回全部供应商(含 is_enabled=false)及其完整配置。

字段类型说明
idstring (UUID)供应商 ID
namestring唯一标识,须匹配 ^[a-z][a-z0-9_-]*$
protocolstringoauth2、oidc、saml2 或 cas
display_namestring展示名称,最长 100 字符
icon_urlstring | null图标地址,必须 http(s):// 开头
button_textstring | null按钮文案,最长 50 字符
configobject协议相关配置(见下)
attribute_mappingobject供应商属性到用户字段的映射,如 {"email": "email"}
is_enabledboolean是否启用
allow_signupboolean是否允许自动创建用户
require_approvalboolean新用户是否需要管理员审批
default_role_idstring (UUID) | null新 SSO 用户的默认角色
created_at / updated_atstring (ISO 8601)创建与更新时间

config 按协议不同:

协议典型字段
oauth2 / oidcclient_id、client_secret、issuer_url、authorization_url、token_url、userinfo_url、scopes
saml2sp_entity_id、idp_entity_id、sso_url、slo_url、x509_cert、acs_url
casserver_url、service_url、version

5.2 创建供应商

POST /api/v1/admin/sso/providers

需要 admin:sso:update。请求体字段与上表一致(name、protocol、display_name、config 必填,attribute_mapping 默认 {},is_enabled 默认 true,allow_signup 默认 true,require_approval 默认 false)。

curl -X POST "https://your-domain.com/api/v1/admin/sso/providers" \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "github",
    "protocol": "oauth2",
    "display_name": "GitHub",
    "config": {"client_id": "...", "client_secret": "...", "authorization_url": "https://github.com/login/oauth/authorize", "token_url": "https://github.com/login/oauth/access_token", "userinfo_url": "https://api.github.com/user", "scopes": ["read:user", "user:email"]},
    "attribute_mapping": {"email": "email", "username": "login"},
    "is_enabled": true,
    "allow_signup": true,
    "require_approval": false
  }'

错误:

HTTP错误码说明
4006305sso_provider_name_exists:标识已被占用
4221001name 不符合命名规则,或 icon_url 不是 http(s) 地址

5.3 更新供应商

PUT /api/v1/admin/sso/providers/{provider_id}

需要 admin:sso:update。所有字段可选,仅更新请求体中出现的字段;name 冲突同样返回 6305。

错误:

HTTP错误码说明
4006300sso_provider_not_found
4006305sso_provider_name_exists

5.4 删除供应商

DELETE /api/v1/admin/sso/providers/{provider_id}

需要 admin:sso:update。删除供应商会同时删除其配置。

错误: 400 + 6300(sso_provider_not_found)。

5.5 测试供应商

POST /api/v1/admin/sso/providers/{provider_id}/test

需要 admin:sso:update。用测试用的 state 与回调地址尝试生成授权 URL,验证配置是否可用。

成功响应(200 OK):

{
  "code": 0,
  "data": {
    "status": "success",
    "message": "SSO provider configuration is valid",
    "authorization_url": "https://github.com/login/oauth/authorize?client_id=..."
  },
  "msg": "success"
}

配置无效时仍返回 200 OK,但 data.status 为 "error",message 为通用错误文案,且不含 authorization_url。

测试结果以 data.status 表达,不通过 HTTP 状态码传递:code: 0 + status: "error" 表示配置有问题。仅在供应商不存在时返回 400 + 6300。

5.6 解绑任意用户的连接

DELETE /api/v1/admin/sso/connections/{connection_id}

需要 admin:sso:update。语义与用户自助解绑一致,但可操作任意用户的连接;同样受“无密码且为唯一登录方式”的保护。

错误:

HTTP错误码说明
4004000sso_connection_not_found
4001004cannot_disconnect_only_auth_method

错误处理

HTTP错误码触发场景
4033000缺少 admin:sso:read / admin:sso:update 权限
4001004解绑后账号将无可用登录方式
4006300供应商不存在(管理端点)
4006305供应商标识重复
4004000SSO 连接不存在或不属于调用者
4221001请求校验失败(供应商命名、icon_url 格式等)

登录/回调流程不使用统一信封,失败通过前端 /sso-callback?error=... 的 error 参数返回:sso_provider_not_found、sso_session_expired、sso_login_failed、pending_approval、inactive。

相关文档

这篇文章对你有帮助吗?

本页目录