OpenID Connect
OpenID Connect(OIDC)是基于 OAuth 2.0 协议的身份认证层。它在 OAuth 2.0 的授权功能之上,添加了标准化的身份认证能力,允许客户端验证用户身份并获取用户基本信息。
为什么需要 OpenID Connect?
OAuth 2.0 是一个授权协议,它解决了"第三方应用如何安全地访问用户资源"的问题。但 OAuth 2.0 并没有标准化用户身份信息的传递方式。
OpenID Connect 解决了这个问题:
- 标准化身份认证 - 统一的用户信息获取方式
- 单点登录(SSO) - 一次登录,多处访问
- 身份提供商生态 - 支持 Google、Microsoft、Apple 等主流身份提供商
- 简化开发 - 无需为每个身份提供商写不同的集成代码
核心概念
ID Token
ID Token 是 OpenID Connect 引入的核心概念,它是一个包含用户身份信息的 JWT。
ID Token 结构:
{
"iss": "https://accounts.google.com",
"sub": "1234567890",
"aud": "your-client-id",
"exp": 1234567890,
"iat": 1234567800,
"name": "John Doe",
"email": "[email protected]",
"picture": "https://example.com/photo.jpg"
}
标准声明(Standard Claims):
| 声明 | 说明 |
|---|---|
sub | 用户的唯一标识符(Subject) |
name | 用户全名 |
given_name | 名 |
family_name | 姓 |
email | 电子邮件地址 |
email_verified | 邮箱是否已验证 |
picture | 头像 URL |
updated_at | 信息最后更新时间 |
与 OAuth 2.0 的关系
┌─────────────────────────────────────────────────────┐
│ OpenID Connect │
│ ┌─────────────────────────────────────────────┐ │
│ │ OAuth 2.0 │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ HTTP + TLS │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
- OAuth 2.0 提供授权框架
- OpenID Connect 在之上添加身份认证层
认证流程
授权码模式(Authorization Code Flow)
最常用、最安全的流程,适用于有后端的应用。
┌─────────┐ ┌─────────┐
│ Client │ │ OpenID │
│ │ │ Provider│
└────┬────┘ └────┬────┘
│ │
│ 1. 认证请求 (scope 包含 openid) │
│ ───────────────────────────────────────────> │
│ │
│ 2. 用户登录并授权 │
│ │
│ 3. 返回授权码 │
│ <─────────────────────────────────────────── │
│ │
│ 4. 用授权码交换令牌 │
│ ───────────────────────────────────────────> │
│ │
│ 5. 返回 ID Token + Access Token │
│ <─────────────────────────────────────────── │
│ │
│ 6. 可选:使用 Access Token 获取用户信息 │
│ ───────────────────────────────────────────> │
│ │
│ 7. 返回用户信息 │
│ <─────────────────────────────────────────── │
隐式模式(Implicit Flow)
⚠️ 已被标记为不安全,不推荐使用。
混合模式(Hybrid Flow)
结合了授权码模式和隐式模式的特点,某些场景下使用。
Discovery 端点
OpenID Connect 提供 Discovery 机制,客户端可以通过一个 URL 获取所有配置信息。
Discovery URL 格式:
https://{issuer}/.well-known/openid-configuration
示例(Google):
https://accounts.google.com/.well-known/openid-configuration
返回的配置信息:
{
"issuer": "https://accounts.google.com",
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
"scopes_supported": ["openid", "email", "profile"],
"response_types_supported": ["code", "token", "id_token"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"id_token_signing_alg_values_supported": ["RS256"]
}
常用 Scope
| Scope | 说明 |
|---|---|
openid | 必需,表示使用 OpenID Connect |
profile | 访问用户基本资料(姓名、头像等) |
email | 访问用户邮箱地址 |
address | 访问用户地址信息 |
phone | 访问用户电话号码 |
offline_access | 获取 Refresh Token |
单点登录(SSO)
OpenID Connect 是实现单点登录的标准方案。
SSO 流程
┌─────────┐ ┌─────────┐ ┌─────────┐
│ App A │ │ IdP │ │ App B │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ 1. 登录 │ │
│ ─────────────>│ │
│ │ │
│ 2. 认证成功 │ │
│ <─────────────│ │
│ │ │
│ 3. 访问 App B│ │
│ │ │
│ │ 4. 自动登录 │
│ │ <─────────────│
│ │ │
│ │ 5. 已认证 │
│ │ ─────────────>│
登出(Logout)
OpenID Connect 定义了两种登出方式:
- RP-Initiated Logout - 客户端发起的登出
- Session Management - 会话管理,检测登录状态变化
身份提供商(IdP)
主流身份提供商
| 提供商 | Discovery URL |
|---|---|
| https://accounts.google.com/.well-known/openid-configuration | |
| Microsoft | https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration |
| Apple | https://appleid.apple.com/.well-known/openid-configuration |
| Auth0 | https://{tenant}.auth0.com/.well-known/openid-configuration |
| Okta | https://{tenant}.okta.com/.well-known/openid-configuration |
自建身份提供商
- Keycloak - 开源,功能丰富
- Authelia - 轻量级,适合家庭/小团队
- Casdoor - 国产开源方案
安全最佳实践
验证 ID Token
必须验证以下声明:
iss- 发行者是否可信aud- 受众是否为自己的 client_idexp- 令牌是否过期iat- 签发时间是否合理- 签名 - 使用 IdP 的公钥验证
使用 PKCE
所有客户端(包括机密客户端)都应该使用 PKCE。
状态管理
- 使用
state参数防止 CSRF - 使用
nonce防止重放攻击
OAuth 2.0 vs OpenID Connect
| 特性 | OAuth 2.0 | OpenID Connect |
|---|---|---|
| 主要目的 | 授权 | 认证 |
| 令牌类型 | Access Token | Access Token + ID Token |
| 用户信息 | 通过 API 获取 | 包含在 ID Token 中 |
| 标准化 | 授权协议 | 认证层(基于 OAuth 2.0) |
| 使用场景 | 第三方授权 | 单点登录、身份认证 |
| Discovery | 不支持 | 支持 |
| 登出机制 | 无标准 | 标准登出流程 |
代码实现
Node.js (openid-client)
const { Issuer, generators } = require('openid-client');
// Discovery - 自动获取配置
const googleIssuer = await Issuer.discover('https://accounts.google.com');
const client = new googleIssuer.Client({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
redirect_uris: ['http://localhost:3000/callback'],
response_types: ['code']
});
// 登录
app.get('/login', (req, res) => {
const nonce = generators.nonce();
const state = generators.state();
req.session.nonce = nonce;
req.session.state = state;
const url = client.authorizationUrl({
scope: 'openid email profile',
nonce,
state
});
res.redirect(url);
});
// 回调
app.get('/callback', async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback(
'http://localhost:3000/callback',
params,
{ nonce: req.session.nonce, state: req.session.state }
);
// 验证 ID Token
const claims = tokenSet.claims();
console.log('User:', claims.sub, claims.email);
res.redirect('/dashboard');
});
Python (python-oauth2)
from authlib.integrations.requests_client import OAuth2Session
# 配置 OIDC
client = OAuth2Session(
client_id=os.getenv('GOOGLE_CLIENT_ID'),
client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
scope='openid email profile'
)
@app.route('/login')
def login():
uri, state = client.create_authorization_url(
'https://accounts.google.com/o/oauth2/v2/auth'
)
session['oauth_state'] = state
return redirect(uri)
@app.route('/callback')
def callback():
token = client.fetch_token(
'https://oauth2.googleapis.com/token',
authorization_response=request.url,
state=session['oauth_state']
)
# 获取用户信息
userinfo = client.get('https://openidconnect.googleapis.com/v1/userinfo').json()
return redirect('/dashboard')
小结
-
核心要点
- OpenID Connect 在 OAuth 2.0 之上添加身份认证
- ID Token 是核心概念,包含用户身份信息
- 支持 Discovery,简化配置
-
主要优势
- 标准化的身份认证
- 单点登录(SSO)
- 丰富的身份提供商生态
- 简化多身份源集成
-
适用场景
- 第三方登录(微信、GitHub、Google 等)
- 企业内部单点登录
- 多应用统一身份管理
- 零信任架构身份验证