指南
授权码流程
OAuth 2.0 Authorization Code + PKCE
授权码流程
OAuth 2.0 授权码流程(Authorization Code Grant)是最安全的授权方式:访问令牌只出现在后端,浏览器永不直接接触。
流程概览
客户端 授权服务器 用户
| | |
|--- 1. 授权请求 ------->| |
| |----- 2. 登录/同意 ->|
|<------ 3. 授权码跳转 --| |
|--- 4. 令牌请求 ------->| |
|<------- 5. 令牌响应 ---| |
- 客户端将用户重定向到授权端点。
- 用户在授权服务器登录并授权。
- 授权服务器将用户带回
redirect_uri并携带授权码。 - 客户端后端用授权码交换令牌。
- 授权服务器返回访问令牌与刷新令牌。
授权端点
GET /api/oauth/authorize,参数全部位于查询字符串:
| 参数 | 必填 | 说明 |
|---|---|---|
response_type |
是 | 固定 code |
client_id |
是 | 控制台注册的客户端标识 |
redirect_uri |
是 | 必须与控制台登记的一致 |
scope |
否 | 空格分隔,如 openid profile email。offline_access 可选,不决定是否签发刷新令牌 |
state |
是 | 防 CSRF,必须携带 |
nonce |
条件 | 请求含 openid 时必须携带,防 ID Token 重放 |
code_challenge |
是 | PKCE 挑战值 |
code_challenge_method |
是 | 固定 S256 |
prompt |
否 | none / login / consent |
max_age |
否 | 认证最多可距今多少秒,超时需重新登录 |
PKCE 强制要求
- 每次授权请求必须生成新的
code_verifier与code_challenge。 code_verifier43–128 位,使用[A-Za-z0-9\-._~]字符集。code_challenge = base64url(sha256(code_verifier))。- 令牌请求中
code_verifier与授权请求中的code_challenge必须匹配,否则拒绝。
令牌端点
POST /api/oauth/token,表单编码。交换授权码:
POST /api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&client_id=CLIENT_ID
&code_verifier=VERIFIER
机密客户端还需要 client_secret。成功响应始终包含 refresh_token(与是否请求 offline_access 无关)。请保存该值,并在 expires_in(默认 1 小时)到期前续期。刷新令牌轮换及宽限见 令牌管理。
常见错误
授权端(/api/oauth/authorize)不会返回 JSON。redirect_uri 合法时,错误以 302 带回客户端(error、error_description、state、iss);无法安全回跳时进入本站 /auth/error。
| 场景 | 授权端 | 令牌端 POST /api/oauth/token |
|---|---|---|
| 参数缺失或不合法 | 302 invalid_request 或本站错误页 |
400 invalid_request |
| 客户端不存在、已停用或回调未登记 | 本站错误页 | 401 invalid_client |
PKCE 缺失或 code_challenge 格式无效 |
302 invalid_request |
— |
code_verifier 不匹配 |
— | 400 invalid_grant(授权码仍未消费,可重试) |
| 授权码已使用、过期或账号已不可用 | — | 400 invalid_grant |
| 请求的 scope 超出应用登记 | 302 invalid_scope |
— |
| 限流 | 本站错误页 | 429 temporarily_unavailable |
令牌端错误响应体符合 RFC 6749:
{
"error": "invalid_grant",
"error_description": "Authorization code expired or already used"
}
安全要点
- 必须携带并校验回调中的
state,避免 CSRF。 - 请求
openid时必须携带nonce,并在 ID Token 中校验。 redirect_uri必须精确匹配登记值;登记时非本机仅允许 https。- 授权码一次性使用、短期有效(仅存哈希)。
- 令牌只经后端交换,不要在前端暴露
client_secret。 - 用刷新令牌续期,不要把访问令牌寿命当成第三方网站的登录会话寿命。