Frontend Academy · 课时

前端 OAuth 流程

在 SPA 中使用 PKCE 实现授权码流程,交换代码获取令牌,安全地存储访问令牌,并避免使用隐式流程。

第 4 / 4 课15 个步骤

前端 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. XSS 防护:输出编码与 CSP
  2. CSRF:SameSite Cookie 与令牌
  3. 内容安全策略:随机数与哈希
  4. 前端 OAuth 流程
← 返回 Frontend Academy