SSO API
单点登录流程、连接解绑与统一集成管理端
SSO API 覆盖三件事:登录页的登录集成发现、基于浏览器重定向的单点登录流程(OAuth2 / OIDC、SAML2、CAS、飞书),以及用户解绑自己的 SSO 身份。集成的新增、配置与测试属于统一集成管理,挂在 /api/v1/admin/integrations。
前置条件与认证
GET /api/v1/sso/integrations、GET /api/v1/sso/login/{provider_name}、GET /api/v1/sso/callback/{provider_name} 是公开端点,不需要认证。login 与 callback 返回重定向而非统一信封。
DELETE /api/v1/sso/connections/{connection_id} 需要登录用户(只能解绑自己的连接)。
管理端基地址为 /api/v1/admin/integrations:登录域操作需要 admin:integrations:login:read / admin:integrations:login:update;内置配置(邮件、Webhook、飞书目录同步等)需要 admin:integrations:settings:read / admin:integrations:settings:update。目录与列表接口接受任一读域,列表按已授权的域过滤实例。
集成配置(config、attribute_mapping、credentials、login_config 的边界)见 SSO 配置。
login 与 callback 返回 HTTP 307 重定向,不是 {code, data, msg} 信封。前端在 /sso-callback 路由上接收 token 或 error 查询参数。若站点设置 sso_enabled 为 false,GET /sso/integrations 返回空 data 数组,即使已配置集成也不会暴露。
端点总览
| 方法 | 路径 | 用途 | 认证 |
|---|---|---|---|
| GET | /api/v1/sso/integrations | 列出已启用的登录集成(登录页使用) | 公开 |
| 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/integrations/types | 静态集成类型、能力与单例标记 | 任一读域 |
| GET | /api/v1/admin/integrations | 按授权域过滤的集成列表 | 任一读域 |
| POST | /api/v1/admin/integrations | 创建非单例登录集成 | 登录域 update |
| PUT / DELETE | /api/v1/admin/integrations/{id} | 按字段鉴权更新 / 删除登录集成(不删单例) | 见下 |
| POST | /api/v1/admin/integrations/{id}/test | 测试登录配置 | 登录域 update |
| DELETE | /api/v1/admin/integrations/connections/{connection_id} | 解绑任意用户的 SSO 连接 | admin:integrations:login:update |
| GET / PUT | /api/v1/admin/integrations/login-settings | 全局登录设置(入口:站点设置 > 安全) | 登录域读 / 写 |
| GET / PUT | /api/v1/admin/integrations/email、/webhook | 内置通知配置 | 设置域读 / 写 |
| POST | /api/v1/admin/integrations/email/test、/webhook/test | 测试通知投递 | 设置域 update |
| — | 飞书与微信 iLink 的专用端点 | 配置、目录/知识同步、回调、个人偏好 | 见 消息集成 API |
1. 列出已启用的登录集成
GET /api/v1/sso/integrations登录页用它渲染 SSO 按钮。只返回已启用 且 enabled_capabilities 包含 user_login 的集成;已启用但未显式开启登录能力的飞书集成不会出现。
成功响应(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",
"provider_type": "oauth2"
}
],
"msg": "success"
}| 字段 | 类型 | 说明 |
|---|---|---|
id | string (UUID) | 集成 ID(升级迁移保留原 UUID) |
name | string | 集成唯一标识(登录 URL 中使用) |
display_name | string | 展示名称 |
icon_url | string | null | 图标地址 |
button_text | string | null | 按钮文案 |
provider_type | string | oauth2、oidc、saml2、cas 或 feishu |
2. 发起 SSO 登录
GET /api/v1/sso/login/{provider_name}查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
redirect | string | 否 | 登录成功后要落到的同源相对路径(如 /app/kb)。非相对路径或站外值一律被忽略并回退到 /app,对所有角色一致 |
行为:
- 校验集成存在且已启用,否则直接重定向到前端
{frontend}/sso-callback?error=sso_provider_not_found。 - 创建一个 10 分钟后过期的临时 SSO 会话,保存协议状态(PKCE
code_verifier/nonce或中继状态)。 - 回调地址由后端来源拼接:
{backend}/api/v1/sso/callback/{provider_name}。 - 返回
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 / oidc / feishu | code、state |
cas | ticket(可带 state) |
saml2 | SAMLResponse、RelayState |
在身份提供商处登记的回调地址为 https://<backend-host>/api/v1/sso/callback/{name}。飞书应用还需要登录权限 contact:user.base:readonly contact:user.email:readonly;回调使用 PKCE 换取授权码,并把用户资料的 union_id(取不到时回退到 open_id)存为 provider_user_id。
成功 → 浏览器重定向到:
{frontend}/sso-callback?token=<access_token>&redirect=<path>token 是标准 JWT 访问令牌(见认证与登录 API)。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_id | string (UUID) | 是 | 用户 SSO 连接 ID |
只能操作属于当前用户的连接;不属于调用者的连接返回 404(sso_connection_not_found)。若账号没有设置密码,且这是最后一条 SSO 连接,删除会被 403 拒绝(cannot_disconnect_only_auth_method),否则账号将无法登录。
当前用户的连接列表可从 GET /api/v1/users/me 的 sso_connections 字段读取,见 用户 API。连接字段使用 integration_ 前缀命名:integration_id、integration_name、integration_display_name、integration_icon_url;provider_user_id 仍是外部身份主键。
成功响应(200 OK):
{
"code": 0,
"data": null,
"msg": "success"
}错误:
| HTTP | 错误码 | 说明 |
|---|---|---|
404 | 4000 | sso_connection_not_found:连接不存在或不属于当前用户 |
403 | 1004 | cannot_disconnect_only_auth_method:无密码且这是唯一登录方式 |
5. 管理端:统一集成
管理基地址为 /api/v1/admin/integrations。登录域操作需要 admin:integrations:login:read / admin:integrations:login:update;内置配置需要 admin:integrations:settings:read / admin:integrations:settings:update。目录(/types)与列表接受任一读域,列表按已授权的域过滤实例。
旧管理命名空间已移除
旧的 /api/v1/admin/sso/* 管理端点与 /sso/providers 发现端点已移除,请使用本节的统一集成端点。登录 / 回调 URL 保持不变;升级迁移保留 UUID 与已绑定的身份。
5.1 创建与更新字段
创建 / 更新请求体字段:name、provider_type、display_name、icon_url、button_text、is_enabled、enabled_capabilities、config、attribute_mapping、credentials、login_config。注册策略(allow_signup、require_approval、default_role_id)属于 login_config。
密钥只能以明文放在 credentials 中传入,落盘时加密(Fernet),且永不返回:管理端输出完全省略 credentials,改为暴露 credentials_configured 与 supported_capabilities。传入空密钥表示保留已存储的值。
{
"name": "github",
"provider_type": "oauth2",
"display_name": "GitHub",
"is_enabled": true,
"enabled_capabilities": ["user_login"],
"config": {
"client_id": "client-id",
"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"
},
"credentials": {"client_secret": "client-secret"},
"attribute_mapping": {"email": "email", "username": "login", "avatar_url": "avatar_url"},
"login_config": {"allow_signup": true, "require_approval": false}
}注册表静态支持:OAuth2 / OIDC / SAML2 / CAS 的登录能力,邮件 / Webhook 的通知能力,以及飞书的目录同步 + 登录能力——其中飞书登录是显式可选能力,需通过 enabled_capabilities 开启。内置集成是单例,使用各自的专用配置端点;飞书作为单例不会出现在新建登录集成的协议列表中。
5.2 按字段鉴权(PUT /{id})
PUT /{id} 按字段而非按实例鉴权:
enabled_capabilities与is_enabled按所涉能力的域收费(user_login归登录域,directory_sync归设置域);- 内置集成的
config与credentials归设置域; - 登录能力类型的
button_text、display_name、icon_url、login_config、attribute_mapping归登录域(非登录能力类型则归设置域)。
同一套字段规则适用于飞书:为飞书开启登录即在通用端点提交 {"enabled_capabilities": ["directory_sync", "user_login"], "is_enabled": true},需要 admin:integrations:login:update。/feishu/* 专用端点仍归设置域。
PUT / DELETE /{id} 只能操作登录集成,不能删除单例内置集成。
5.3 测试配置
POST /api/v1/admin/integrations/{id}/test需要登录域 update。验证登录配置是否可用。
成功响应(200 OK):
{
"code": 0,
"data": {
"status": "success",
"message": "SSO provider configuration is valid"
},
"msg": "success"
}配置测试响应会省略授权地址,避免泄露查询参数或凭据。配置无效时仍返回 200 OK,但 data.status 为 "error"。
测试结果以 data.status 表达,不通过 HTTP 状态码传递:code: 0 + status: "error" 表示配置有问题。
5.4 解绑任意用户的连接
DELETE /api/v1/admin/integrations/connections/{connection_id}需要 admin:integrations:login:update。语义与用户自助解绑一致,但可操作任意用户的连接;同样受“无密码且为唯一登录方式”的保护。
错误:
| HTTP | 错误码 | 说明 |
|---|---|---|
400 | 4000 | sso_connection_not_found |
400 | 1004 | cannot_disconnect_only_auth_method |
错误处理
| HTTP | 错误码 | 触发场景 |
|---|---|---|
403 | 3000 | 缺少所需权限(admin:integrations:login:* / admin:integrations:settings:*) |
400 | 1004 | 解绑后账号将无可用登录方式 |
400 | 6300 | 集成不存在(管理端点) |
400 | 6305 | 集成标识重复 |
400 / 404 | 4000 | SSO 连接不存在或不属于调用者 |
422 | 1001 | 请求校验失败(集成命名、icon_url 格式等) |
登录/回调流程不使用统一信封,失败通过前端 /sso-callback?error=... 的 error 参数返回:sso_provider_not_found、sso_session_expired、sso_login_failed、pending_approval、inactive。
相关文档
这篇文章对你有帮助吗?