前端 OAuth 流程
在 SPA 中使用 PKCE 实现授权码流程,交换代码获取令牌,安全地存储访问令牌,并避免使用隐式流程。
前端 OAuth 流程 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。
什么是 OAuth?
OAuth 2.0 是一种授权协议:您的应用可以获得代表用户在第三方服务(Google、GitHub 等)上执行操作的权限,而无需接触用户的密码。它用于“使用 Google 登录”、GitHub OAuth 应用以及大多数现代身份验证场景。
关键角色
资源所有者:用户。客户端:您的应用。授权服务器:OAuth 提供商(Google、Auth0)。资源服务器:由访问令牌保护的 API。
授权码流程
这是服务器端应用使用的标准安全流程:1)将用户重定向到提供商的登录页面。2)用户登录并同意授权。3)提供商携带 code 重定向回来。4)服务器将 code 交换为访问令牌(仅服务器持有的机密信息)。
SPA 为什么需要 PKCE
SPA 无法安全保存客户端机密信息,因为它会出现在浏览器包中。PKCE(代码交换证明密钥)使用每个请求独有的代码验证器和挑战值来替代该机密信息。如今,PKCE 已成为所有 OAuth 公共客户端的标准。
PKCE 流程逐步说明
1)生成随机的 code_verifier。2)对其进行 SHA-256 哈希,得到 code_challenge。3)携带挑战值重定向到 /authorize。4)用户同意后,获取返回的 code。5)使用 code + 验证器交换访问令牌。提供商会验证挑战值是否等于 verifier 的哈希值。
生成代码验证器和挑战值
验证器是一个随机字符串;挑战值是对验证器进行 SHA-256 哈希后,再进行 Base64 URL 编码的结果。
function base64url(arr) {
return btoa(String.fromCharCode(...arr))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
async function generatePKCE() {
const arr = new Uint8Array(32);
crypto.getRandomValues(arr);
const verifier = base64url(arr);
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
const challenge = base64url(new Uint8Array(hash));
return { verifier, challenge };
}重定向到提供商
使用挑战值、state(CSRF 防护)和作用域构建授权 URL。
const { verifier, challenge } = await generatePKCE();
const state = crypto.randomUUID();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);
const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
url.searchParams.set('client_id', CLIENT_ID);
url.searchParams.set('redirect_uri', `${origin}/auth/callback`);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
url.searchParams.set('state', state);
window.location.href = url.toString();处理回调
提供商会携带 ?code=...&state=... 重定向到您的回调 URL。请验证 state 以防止 CSRF,然后交换 code。
// /auth/callback page:
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const expected = sessionStorage.getItem('oauth_state');
if (returnedState !== expected) throw new Error('Bad state');
const verifier = sessionStorage.getItem('pkce_verifier');
const tokens = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: CLIENT_ID,
redirect_uri: `${origin}/auth/callback`,
code_verifier: verifier
})
}).then(r => r.json());
// tokens: { access_token, id_token, refresh_token, expires_in }安全存储令牌
最佳实践:不要将访问令牌存储在 localStorage 中(可能被 XSS 窃取)。可选方案:1)仅存储在内存中(刷新页面后清除,需要重新进行身份验证)。2)使用 HttpOnly Cookie(由您的后端在 PKCE 流程完成后设置,JS 无法窃取)。
避免使用隐式流程
旧版隐式流程会直接在 URL 片段中返回访问令牌。根据 OAuth 2.0 Security BCP,该流程已被弃用,因为令牌会泄露到浏览器历史记录和引用来源中。请始终使用授权码 + PKCE。
刷新令牌
访问令牌会过期(通常为 1 小时)。刷新令牌的有效期更长,可以静默获取新的访问令牌。对于 SPA,刷新令牌越来越多地通过 HttpOnly Cookie 提供,绝不要存储在 localStorage 中。
ID 令牌与访问令牌
ID 令牌(OpenID Connect):用于证明用户身份的 JWT。访问令牌:可以是 opaque 值或 JWT,用于调用 API。在信任 ID 令牌之前,请验证其签名和声明(iss、aud、exp、nonce)。
身份验证库
不要自行实现。请使用:原始 OIDC 使用 oidc-client-ts,Auth0 使用 auth0/spa-js,Clerk 使用 @clerk/clerk-react,Next 使用 NextAuth.js/Auth.js,Nuxt 使用 Nuxt-auth。它们会处理 PKCE、刷新和存储。
快速检查
为什么单页应用必须在授权码流程中使用 PKCE(代码交换证明密钥),而不能只使用基本的授权码流程?
回顾:前端中的 OAuth
授权码 + PKCE 是 SPA 的现代标准。每个请求都生成验证器 + S256 挑战值。携带挑战值 + state 重定向到 /authorize。在回调时验证 state。使用 code + 验证器交换令牌。绝不要使用隐式流程。令牌存储优先使用 HttpOnly Cookie。请使用库(auth0-spa-js、oidc-client-ts、NextAuth),不要自行实现。
用 AI 导师学习 HTML — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 41
- 课程
- 163
常见问题解答
「前端 OAuth 流程」课时是免费的吗?
是的 — 「前端 OAuth 流程」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。
「前端 OAuth 流程」这节课中我会学到什么?
在 SPA 中使用 PKCE 实现授权码流程,交换代码获取令牌,安全地存储访问令牌,并避免使用隐式流程。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Frontend Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「前端 OAuth 流程」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Frontend Academy 课中编写并运行代码吗?
能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。