name: OAuth实现 description: OAuth 2.0和OpenID Connect认证与安全流程。用于第三方集成、SSO系统、基于令牌的API访问,或遇到授权码流程、PKCE、令牌刷新、范围管理错误。
OAuth实现
实现OAuth 2.0和OpenID Connect以进行安全认证。
OAuth 2.0流程
| 流程 | 用例 |
|---|---|
| 授权码 | Web应用(最安全) |
| 授权码 + PKCE | SPA、移动应用 |
| 客户端凭证 | 服务间通信 |
| 刷新令牌 | 会话续期 |
授权码流程(Express)
const express = require('express');
const jwt = require('jsonwebtoken');
// 步骤1:重定向到授权
app.get('/auth/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const params = new URLSearchParams({
client_id: process.env.CLIENT_ID,
redirect_uri: process.env.REDIRECT_URI,
response_type: 'code',
scope: 'openid profile email',
state
});
res.redirect(`${PROVIDER_URL}/authorize?${params}`);
});
// 步骤2:处理回调
app.get('/auth/callback', async (req, res) => {
if (req.query.state !== req.session.oauthState) {
return res.status(400).json({ error: 'Invalid state' });
}
const tokenResponse = await fetch(`${PROVIDER_URL}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: req.query.code,
redirect_uri: process.env.REDIRECT_URI,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET
})
});
const tokens = await tokenResponse.json();
// 安全存储令牌并创建会话
});
PKCE用于公共客户端
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}
安全要求
- 始终使用HTTPS
- 严格验证重定向URI
- 对公共客户端使用PKCE
- 在HttpOnly cookie中存储令牌
- 实现令牌轮换
- 使用短期访问令牌(15分钟)
附加实现
参见references/python-java.md了解:
- 使用Authlib OIDC提供程序的Python Flask
- OpenID Connect发现和JWKS端点
- Java Spring Security OAuth2服务器
- 令牌内省和吊销
永远不要做
- 在localStorage中存储令牌
- 使用隐式流程
- 跳过状态参数验证
- 在前端暴露客户端密钥
- 使用长期访问令牌