ClouisleClouisle

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"
}
字段类型说明
idstring (UUID)集成 ID(升级迁移保留原 UUID)
namestring集成唯一标识(登录 URL 中使用)
display_namestring展示名称
icon_urlstring | null图标地址
button_textstring | null按钮文案
provider_typestringoauth2、oidc、saml2、cas 或 feishu

2. 发起 SSO 登录

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

查询参数

参数类型必填说明
redirectstring否登录成功后要落到的同源相对路径(如 /app/kb)。非相对路径或站外值一律被忽略并回退到 /app,对所有角色一致

行为:

  1. 校验集成存在且已启用,否则直接重定向到前端 {frontend}/sso-callback?error=sso_provider_not_found。
  2. 创建一个 10 分钟后过期的临时 SSO 会话,保存协议状态(PKCE code_verifier / nonce 或中继状态)。
  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 / oidc / feishucode、state
casticket(可带 state)
saml2SAMLResponse、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_idstring (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错误码说明
4044000sso_connection_not_found:连接不存在或不属于当前用户
4031004cannot_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错误码说明
4004000sso_connection_not_found
4001004cannot_disconnect_only_auth_method

错误处理

HTTP错误码触发场景
4033000缺少所需权限(admin:integrations:login:* / admin:integrations:settings:*)
4001004解绑后账号将无可用登录方式
4006300集成不存在(管理端点)
4006305集成标识重复
400 / 4044000SSO 连接不存在或不属于调用者
4221001请求校验失败(集成命名、icon_url 格式等)

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

相关文档

  • 认证与登录 API — 密码登录、JWT 令牌与 TOTP 两步验证
  • SSO 配置 — 各协议的配置项、config / attribute_mapping / credentials / login_config 边界与升级回滚注意
  • 用户 API — 用户资料的 sso_connections 字段
  • 错误处理 — 按 HTTP 状态与业务错误码恢复

这篇文章对你有帮助吗?

本页目录