oauth-implementation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOAuth Implementation
OAuth 实现
Implement OAuth 2.0 and OpenID Connect for secure authentication.
实现OAuth 2.0和OpenID Connect以保障安全认证。
OAuth 2.0 Flows
OAuth 2.0 流程
| Flow | Use Case |
|---|---|
| Authorization Code | Web apps (most secure) |
| Authorization Code + PKCE | SPAs, mobile apps |
| Client Credentials | Service-to-service |
| Refresh Token | Session renewal |
| 流程 | 使用场景 |
|---|---|
| 授权码流程 | Web应用(安全性最高) |
| 授权码流程 + PKCE | 单页应用(SPA)、移动应用 |
| 客户端凭证流程 | 服务间通信 |
| 刷新令牌流程 | 会话续期 |
Authorization Code Flow (Express)
授权码流程(Express 实现)
javascript
const express = require('express');
const jwt = require('jsonwebtoken');
// Step 1: Redirect to authorization
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}`);
});
// Step 2: Handle callback
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();
// Store tokens securely and create session
});javascript
const express = require('express');
const jwt = require('jsonwebtoken');
// Step 1: Redirect to authorization
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}`);
});
// Step 2: Handle callback
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();
// Store tokens securely and create session
});PKCE for Public Clients
面向公开客户端的PKCE实现
javascript
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}javascript
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}Security Requirements
安全要求
- Always use HTTPS
- Validate redirect URIs strictly
- Use PKCE for public clients
- Store tokens in HttpOnly cookies
- Implement token rotation
- Use short-lived access tokens (15 min)
- 始终使用HTTPS
- 严格验证重定向URI
- 为公开客户端使用PKCE
- 将令牌存储在HttpOnly Cookie中
- 实现令牌轮转机制
- 使用短期访问令牌(15分钟)
Additional Implementations
更多实现示例
See references/python-java.md for:
- Python Flask with Authlib OIDC provider
- OpenID Connect discovery and JWKS endpoints
- Java Spring Security OAuth2 server
- Token introspection and revocation
查看 references/python-java.md 获取以下内容:
- 基于Authlib OIDC提供者的Python Flask实现
- OpenID Connect 发现与JWKS端点
- Java Spring Security OAuth2 服务器
- 令牌内省与吊销
Never Do
禁止操作
- Store tokens in localStorage
- Use implicit flow
- Skip state parameter validation
- Expose client secrets in frontend
- Use long-lived access tokens
- 将令牌存储在localStorage中
- 使用隐式流程
- 跳过state参数验证
- 在前端暴露客户端密钥
- 使用长期访问令牌