OIDC 集成指南
OIDC 集成指南
MLiev IAM 作为 OIDC 身份提供商(IdP),为第三方应用提供单点登录(SSO)服务。本文档说明如何将你的应用接入 MLiev IAM。
支持的功能
- 授权码流程(Authorization Code Flow)
- 隐式流程(Implicit Flow)
- 客户端凭据流程(Client Credentials Flow)
- 刷新令牌(Refresh Token)
- PKCE(Proof Key for Code Exchange)
- 用户信息端点(UserInfo)
- 令牌内省(Token Introspection)
- 令牌撤销(Token Revocation)
- OIDC 发现文档
- JWKS 端点
- RS256 / HS256 签名
协议端点
所有 OIDC 端点以 /oidc/:client_id 为前缀:
| 端点 | 方法 | 路径 |
|---|---|---|
| 发现文档 | GET | /oidc/:client_id/.well-known/openid-configuration |
| 授权 | GET/POST | /oidc/:client_id/authorize |
| 令牌 | GET/POST | /oidc/:client_id/token |
| 用户信息 | GET/POST | /oidc/:client_id/userinfo |
| 令牌内省 | POST | /oidc/:client_id/introspect |
| 令牌撤销 | POST | /oidc/:client_id/revoke |
| JWKS | GET | /oidc/:client_id/jwks |
所有端点中的
:client_id是 OIDC 客户端的 Client ID。
第一步:创建 OIDC 客户端
通过管理员 API 创建 OIDC 客户端:
curl -X POST http://localhost:8080/api/admin/applications \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <admin_token>" \
-d '{
"name": "我的应用",
"description": "示例 OIDC 客户端",
"protocolType": "oidc",
"protocolConfig": {
"redirectUris": ["http://localhost:3000/callback"],
"grantTypes": ["authorization_code", "refresh_token"],
"scopes": ["openid", "profile", "email"],
"clientType": "confidential",
"requirePkce": false
}
}'响应中包含 clientId 和 clientSecret,请妥善保管。
第二步:授权码流程
2.1 引导用户到授权页面
将用户浏览器重定向到授权端点:
https://auth.example.com/oidc/{client_id}/authorize?
response_type=code&
client_id={client_id}&
redirect_uri=http://localhost:3000/callback&
scope=openid profile email&
state=随机字符串&
nonce=随机字符串参数说明:
| 参数 | 必填 | 说明 |
|---|---|---|
| response_type | 是 | 固定为 code |
| client_id | 是 | 客户端 ID |
| redirect_uri | 是 | 回调地址(必须在客户端注册的列表中) |
| scope | 是 | 请求的作用域(至少包含 openid) |
| state | 推荐 | 防 CSRF 的随机字符串 |
| nonce | 推荐 | 防重放的随机字符串 |
2.2 用户登录并授权
用户在 IAM 的登录页面完成认证和授权确认后,浏览器会重定向回你的回调地址:
http://localhost:3000/callback?code=授权码&state=随机字符串2.3 用授权码交换令牌
在服务端使用授权码获取令牌:
curl -X POST https://auth.example.com/oidc/{client_id}/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code\
&code=授权码\
&redirect_uri=http://localhost:3000/callback\
&client_id={client_id}\
&client_secret={client_secret}"响应:
{
"access_token": "访问令牌",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "刷新令牌",
"id_token": "eyJhbGciOiJSUzI1NiIs...",
"scope": "openid profile email"
}使用 PKCE(公开客户端推荐)
对于无法安全存储 client_secret 的客户端(如 SPA、移动应用),使用 PKCE 增强安全性:
生成 Code Verifier 和 Challenge
// 生成 code_verifier(43-128字符的随机字符串)
const codeVerifier = generateRandomString(64);
// 计算 code_challenge
const codeChallenge = base64url(sha256(codeVerifier));授权请求
/oidc/{client_id}/authorize?
response_type=code&
client_id={client_id}&
redirect_uri=http://localhost:3000/callback&
scope=openid profile email&
code_challenge={code_challenge}&
code_challenge_method=S256令牌请求
curl -X POST /oidc/{client_id}/token \
-d "grant_type=authorization_code\
&code=授权码\
&redirect_uri=http://localhost:3000/callback\
&client_id={client_id}\
&code_verifier={code_verifier}"获取用户信息
使用 access_token 获取当前用户信息:
curl https://auth.example.com/oidc/{client_id}/userinfo \
-H "Authorization: Bearer {access_token}"响应:
{
"sub": "123",
"name": "张三",
"preferred_username": "zhangsan",
"email": "zhangsan@example.com",
"email_verified": true
}支持的 Scope 和返回字段:
| Scope | 返回字段 |
|---|---|
| openid | sub |
| profile | name, preferred_username, nickname, picture |
| email, email_verified | |
| phone | phone_number, phone_number_verified |
客户端凭据流程
用于服务间认证(不涉及用户):
curl -X POST https://auth.example.com/oidc/{client_id}/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials\
&client_id={client_id}\
&client_secret={client_secret}\
&scope=api"注意:客户端凭据流程不返回
refresh_token和id_token。
刷新令牌
curl -X POST https://auth.example.com/oidc/{client_id}/token \
-d "grant_type=refresh_token\
&refresh_token={refresh_token}\
&client_id={client_id}\
&client_secret={client_secret}"令牌内省
验证令牌是否有效(服务端使用):
curl -X POST https://auth.example.com/oidc/{client_id}/introspect \
-d "token={access_token}\
&client_id={client_id}\
&client_secret={client_secret}"令牌撤销
主动撤销令牌:
curl -X POST https://auth.example.com/oidc/{client_id}/revoke \
-d "token={token}\
&client_id={client_id}\
&client_secret={client_secret}"前端组件嵌入
MLiev IAM 提供可嵌入的登录组件,支持 iframe 方式集成:
iframe 嵌入登录页
<iframe
src="https://auth.example.com/auth/login?session_id=xxx"
width="400"
height="500"
frameborder="0"
></iframe>
<script>
window.addEventListener('message', (event) => {
if (event.data.type === 'oidc-login-success') {
console.log('登录成功:', event.data);
// 处理登录成功回调
}
});
</script>页面路由
| 路径 | 说明 |
|---|---|
/auth/login?session_id={id} |
OIDC 登录页面 |
/auth/consent?session_id={id} |
授权确认页面 |
OIDC 发现文档
客户端可通过发现文档自动获取所有端点配置:
curl https://auth.example.com/oidc/{client_id}/.well-known/openid-configuration常见错误
| 错误 | 说明 | 解决方案 |
|---|---|---|
invalid_client |
客户端 ID 或密钥错误 | 检查 client_id 和 client_secret |
invalid_grant |
授权码无效或已过期 | 授权码 10 分钟内有效,且一次性使用 |
invalid_scope |
请求的作用域无效 | 确保包含 openid,且在客户端允许范围内 |
invalid_redirect_uri |
回调地址不匹配 | URI 必须完全匹配注册的 redirectUris |
兼容标准
- OpenID Connect Core 1.0
- OAuth 2.0(RFC 6749)
- Token Introspection(RFC 7662)
- Token Revocation(RFC 7009)
- PKCE(RFC 7636)