OIDC 流程
如果应用已经支持 OpenID Connect,或者你希望直接使用现有 OIDC 库,可以将 Sudomimus 作为标准 OIDC 提供方接入。Sudomimus 的 OIDC provider 部署在 oidc.sudomimus.com,支持带 PKCE 的授权码流程。你的应用在该流程中是接入方(Relying Party,RP)。
适用场景:
- 你的框架或平台原生支持 OIDC(Next-Auth、Spring Security、Keycloak adapter 等),希望直接把 Sudomimus 接入做 IdP。
- 你要和某个已经只接 OIDC 的伙伴系统对接。
- 你更熟悉 OIDC 的 client、scope 和 ID token 概念,不希望使用 Connect 协议。
如果你从零开始、只想做最小的自定义集成,Connect 协议 通常更短。
Discovery
Section titled “Discovery”Sudomimus 发布标准 OIDC discovery 文档。将库的 issuer 设置为 https://oidc.sudomimus.com 后,客户端库可以自动读取其余配置:
curl https://oidc.sudomimus.com/.well-known/openid-configurationdiscovery 文档声明:
response_types_supported:["code"](仅 authorization code flow)。grant_types_supported:["authorization_code", "refresh_token"]。scopes_supported:["openid", "email", "profile", "offline_access"]。id_token_signing_alg_values_supported:["RS256"]。code_challenge_methods_supported:["S256"]—— 强制 PKCE,不支持 plain。token_endpoint_auth_methods_supported:["private_key_jwt", "client_secret_basic", "client_secret_post", "none"]。
在 with.sudomimus.com 中,为需要启用 OIDC 的应用完成以下配置:
-
添加一条 Layer 3 OIDC 返回规则:
{"returnMethod": "OIDC","payload": {"redirectUris": ["https://app.example.com/oidc/callback"],"postLogoutRedirectUris": ["https://app.example.com/"],"allowedScopes": ["openid", "email", "profile", "offline_access"],"tokenEndpointAuthMethod": "private_key_jwt"}} -
与其他应用一样配置 Layer 1 和 Layer 2:至少添加一条认证规则(例如
PASSKEY_USERNAMELESS或PASSKEY_REASONED)和一条身份准入规则(例如允许特定邮箱的EMAIL规则)。OIDC 流程仍使用平台的同一套用户认证挑战。 -
选择客户端认证方式:
private_key_jwt(推荐用于机密客户端)—— RP 持有私钥,在/token上用 JWT assertion 做客户端认证。签名密钥就是应用的 client-auth 私钥(和 Connect 协议中/establish用的同一把)。client_secret_basic(机密客户端)—— RP 在/token的 HTTPAuthorization: Basic头中携带共享密钥。client_secret_post(机密客户端)—— RP 在/token的表单体中发送共享密钥(client_id+client_secret参数)。none—— 仅限公开客户端(SPA、无后端的移动应用)。必须使用 PKCE。
应用的 applicationAnchor 就是你的 client_id。
OIDC 流程
Section titled “OIDC 流程”1. 授权请求
Section titled “1. 授权请求”把用户浏览器跳转到 /authorize,附带标准 OIDC 参数:
https://oidc.sudomimus.com/authorize ?client_id=my-app &redirect_uri=https%3A%2F%2Fapp.example.com%2Foidc%2Fcallback &response_type=code &scope=openid%20email%20profile &state=<csrf-token> &nonce=<random-nonce> &code_challenge=<S256-of-verifier> &code_challenge_method=S256必填:client_id、redirect_uri、response_type=code、scope(必须包含 openid)、code_challenge、code_challenge_method=S256。
选填(建议带上):state、nonce。
Sudomimus 会将用户跳转到 via.sudomimus.com,完成 Layer 1 允许的认证方式。认证和身份准入检查成功后,浏览器会携带 code 和原始 state 返回 redirect_uri。
2. 令牌交换
Section titled “2. 令牌交换”向 /token 提交授权码。按照 OIDC 规范,请求体必须使用 application/x-www-form-urlencoded:
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$AUTH_CODE" \ --data-urlencode "redirect_uri=https://app.example.com/oidc/callback" \ --data-urlencode "code_verifier=$PKCE_VERIFIER" \ --data-urlencode "client_id=my-app" \ --data-urlencode "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ --data-urlencode "client_assertion=$CLIENT_ASSERTION_JWT"client_assertion 是使用应用 client-auth 私钥签名的 JWT。必需声明包括:iss = client_id、sub = client_id、aud = 完整且精确的 token endpoint URL(生产环境为 https://oidc.sudomimus.com/token)、全新的 jti、iat 和 exp(与 iat 相差不超过 300 秒)。签名算法为 RS256。
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$AUTH_CODE" \ --data-urlencode "redirect_uri=https://app.example.com/oidc/callback" \ --data-urlencode "code_verifier=$PKCE_VERIFIER" \ --data-urlencode "client_id=my-app"不提交 client assertion。PKCE(code_verifier 与授权请求的 code_challenge 匹配)是唯一的客户端认证。
成功响应(JSON):
{ "access_token": "<JWT>", "token_type": "Bearer", "expires_in": 10800, "id_token": "<JWT>", "scope": "openid email profile", "refresh_token": "<JWT —— 只在请求了 'offline_access' 时返回>"}id_token—— 由 Sudomimus 的平台级 OIDC 密钥签发,通过https://oidc.sudomimus.com/.well-known/jwks.json验证。它包含标准 OIDC 声明(iss、sub、aud、exp、iat,以及可选的nonce、email、name)。access_token—— 由应用的 token-signing 密钥签发,结构与 Connect 协议的访问令牌相同。refresh_token—— 仅在请求offline_accessscope 时返回。
3. Userinfo
Section titled “3. Userinfo”curl https://oidc.sudomimus.com/userinfo \ -H "Authorization: Bearer $ACCESS_TOKEN"返回 scope 允许的声明:
{ "sub": "<sector subject>", "email": "<只在授予 'email' scope 时返回>", "name": "<只在授予 'profile' scope 时返回>"}sub 是 扇区主体(sector subject) —— 按扇区维度、应用可见的标识符(与 id_token 的 sub 相同),而非原始账户 UUID。/userinfo 同时支持 GET 与 POST。
4. Refresh
Section titled “4. Refresh”如果请求了 offline_access 并拿到了 refresh token,去 /token 兑换:
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=$REFRESH_TOKEN" \ --data-urlencode "client_id=my-app" # 机密客户端再加 client_assertion可以传入可选的 scope,请求原始授权范围的子集。根据 OIDC §12.1,刷新得到的 ID token 不会包含新的 nonce。
5. 结束会话
Section titled “5. 结束会话”https://oidc.sudomimus.com/end-session ?id_token_hint=<id_token> &client_id=my-app &post_logout_redirect_uri=https%3A%2F%2Fapp.example.com%2F &state=<可选>/end-session 通知 OIDC 会话结束,并把用户跳转到 post_logout_redirect_uri(必须与注册过的某个 URI 完全匹配)。它不会自动吊销 refresh token —— 真正使底层会话失效要调 Connect 的 /logout(单个会话)或 /revoke-all(账户的所有会话)。详见 管理会话。
使用 OIDC 库
Section titled “使用 OIDC 库”绝大多数现代 OIDC 库(Node 的 openid-client、Python 的 authlib、Java 的 nimbus-jose-jwt 等)都能从 issuer URL 自动发现,并自动处理 PKCE、JWKS、令牌验证。一个最小的 Node 示例:
import { Issuer } from "openid-client";
const issuer = await Issuer.discover("https://oidc.sudomimus.com");const client = new issuer.Client({ client_id: "my-app", redirect_uris: ["https://app.example.com/oidc/callback"], response_types: ["code"], token_endpoint_auth_method: "none", // 或 "private_key_jwt"});
// 登录时:const codeVerifier = generators.codeVerifier();const codeChallenge = generators.codeChallenge(codeVerifier);const authUrl = client.authorizationUrl({ scope: "openid email profile", code_challenge: codeChallenge, code_challenge_method: "S256", state, nonce,});
// 回调时:const params = client.callbackParams(req);const tokenSet = await client.callback(redirectUri, params, { code_verifier: codeVerifier, state, nonce });const userinfo = await client.userinfo(tokenSet.access_token);令牌验证提醒
Section titled “令牌验证提醒”OIDC ID token 通过 oidc.sudomimus.com/.well-known/jwks.json 上的 JWKS 验证 —— 这是 OIDC 标准机制,OIDC 库会自动做。
但 /token 返回的 access_token 不是使用平台 JWKS 验证的令牌。它是应用专属的访问令牌,结构与 Connect 流程相同,需要使用 POST /info 返回的 applicationPublicKey 验证。如果客户端库会在本地验证访问令牌,请为它配置 /info 提供的密钥;也可以将访问令牌视为不透明字符串,只通过 /userinfo 获取声明。详见令牌与验证。