5.3 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-07-13 10:03 |
OpenID Connect (OIDC)
概述
OpenID Connect(OIDC)是构建在 OAuth 2.0 之上的身份认证层。如果说 OAuth 2.0 解决「你能访问什么」,OIDC 则补充了「你是谁」。它是当前最主流的 SSO 协议,几乎所有现代身份提供商(Keycloak、Auth0、Okta、Azure AD)都支持 OIDC。
[!info] 为什么需要 OIDC? OAuth 2.0 的授权码拿到的是 access_token,它可以调 API,但你不知道登录的用户是谁。应用只能再去调
/userinfo接口,流程不统一。OIDC 通过引入 ID Token 直接告诉应用用户的身份。
OIDC vs OAuth 2.0 的区别
| 维度 | OAuth 2.0 | OIDC |
|---|---|---|
| 本质 | 授权框架 | 认证 + 授权 |
| Token | access_token, refresh_token | + id_token (JWT) |
| 用户信息 | 需要额外请求 /userinfo | ID Token 自带 + /userinfo 补充 |
| scope | 自定义 | 必须含 openid,标准 scope: profile, email, address, phone |
| 规范 | RFC 6749 | OpenID Connect Core 1.0 |
核心流程
OIDC 的授权码流程和 OAuth 2.0 几乎一致,区别在于 scope 包含 openid,返回值多了 id_token:
sequenceDiagram
participant U as 用户
participant App as Client (RP)
participant IdP as OpenID Provider
U->>App: 1. 点击登录
App->>U: 2. 302 → IdP
Note right of App: scope=openid profile email
U->>IdP: 3. 认证 + 授权
IdP->>U: 4. 302 → redirect_uri
Note left of IdP: code=xxx & state=yyy
U->>App: 5. 回调
App->>IdP: 6. POST /token
IdP-->>App: 7. { access_token, id_token, refresh_token }
App->>App: 8. 验证 id_token 签名 + claims
App-->>U: 9. 登录成功
ID Token 详解
ID Token 是一个 JWT(JSON Web Token),包含用户身份信息,由 IdP 用私钥签名。
结构
JWT 由三部分组成:Header.Payload.Signature
Header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-2026"
}
Payload(Claims):
{
"iss": "https://idp.example.com",
"sub": "user-12345",
"aud": "your-client-id",
"exp": 1752374580,
"iat": 1752374280,
"nonce": "random-nonce-value",
"name": "张三",
"email": "zhangsan@example.com",
"email_verified": true,
"picture": "https://cdn.example.com/avatar.jpg"
}
必须校验的 Claims
[!danger] 不校验 = 门户大开 每一个 Claim 都有其安全意义,跳过任何一个都可能导致身份伪造。
| Claim | 校验规则 | 不校验的风险 |
|---|---|---|
iss |
必须是你配置的 IdP 地址 | 接受恶意 IdP 签发的 Token |
aud |
必须包含你的 client_id |
Token 被别的应用盗用 |
exp |
必须未过期 | 过期 Token 仍可使用 |
nonce |
必须和请求时一致 | 重放攻击 |
signature |
用 IdP 公钥验证 | Token 内容被篡改 |
校验流程
// 使用 OIDC 库自动校验 ID Token
provider, err := oidc.NewProvider(ctx, "https://idp.example.com")
verifier := provider.Verifier(&oidc.Config{
ClientID: "your-client-id",
})
// 解析并校验 ID Token(签名、iss、aud、exp、nonce 全自动校验)
idToken, err := verifier.Verify(ctx, rawIDToken)
// 提取 Claims
var claims struct {
Name string `json:"name"`
Email string `json:"email"`
Picture string `json:"picture"`
}
if err := idToken.Claims(&claims); err != nil {
log.Fatal(err)
}
使用
coreos/go-oidc库时,签名验证会自动从 IdP 的 JWKS 端点(/.well-known/jwks.json)拉取公钥并缓存。
OIDC Discovery
每个 OIDC Provider 都暴露一个发现端点,让客户端自动获取所有配置:
GET https://idp.example.com/.well-known/openid-configuration
返回:
{
"issuer": "https://idp.example.com",
"authorization_endpoint": "https://idp.example.com/oauth/authorize",
"token_endpoint": "https://idp.example.com/oauth/token",
"userinfo_endpoint": "https://idp.example.com/userinfo",
"jwks_uri": "https://idp.example.com/.well-known/jwks.json",
"scopes_supported": ["openid", "profile", "email"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"id_token_signing_alg_values_supported": ["RS256", "ES256"]
}
[!tip] Discovery 的好处 客户端只需要知道 IdP 的 URL,所有端点地址和算法支持都可以自动发现。这极大降低了接入成本。
常见陷阱与最佳实践
永远校验 ID Token 签名
- 使用 IdP 公钥(通过 JWKS 获取)验证,不要只 Base64 解码就信了
- 公钥轮换时,库应自动从 JWKS 端点刷新
nonce 必须使用
- 登录请求生成 nonce 存入 session,ID Token 校验时比对
- 防止 Token 被截获后重放
UserInfo Endpoint 的定位
- ID Token 已包含基本身份信息,UserInfo 用于获取额外数据
- 不要把敏感信息放在 ID Token 里(它可能被前端持有)
多租户场景的 hd 参数
- Google OIDC 支持
hd(hosted domain)限制只允许特定域名登录 - 企业 SSO 场景务必限制,防止个人账号混入
Key Rollover
- IdP 会定期轮换签名密钥,客户端需要支持多 Key(通过
kid匹配) - 推荐使用自动 JWKS 刷新的库,不要硬编码公钥