OAuth 2.0 流程

四种 grant 流程图 + 解释

411 次访问
OAUTH 2.0 FLOW DIAGRAMS

OAuth 2.0 流程图

四种 grant_type · 时序图 · 实战代码

授权码 (Authorization Code)
隐式 (Implicit, 已废弃)
密码 (Resource Owner Password)
客户端凭证 (Client Credentials)
授权码 + PKCE (SPA 推荐)

关于本工具

了解工具定位 · 使用场景 · 对比优势

使用场景

🔑

第三方登录接入

独立开发者或小团队在实现「微信登录」或「GitHub 登录」时,面对授权码模式(Authorization Code)和隐式模式(Implicit)的选择常感困惑。本工具通过可视化流程图,清晰展示两种模式下浏览器、后端服务器与授权服务器之间的跳转与令牌传递路径,帮助开发者快速理解 state 参数防 CSRF 的生效位置,避免因流程理解错误导致的安全漏洞或实现返工。

📱

移动端 SSO 调试

移动 App 开发者在调试「一次登录,多端可用」的 SSO 场景时,常因无法直观看到授权码在 App 内嵌 WebView 与系统浏览器之间的流转而反复抓包。本工具以时序图形式呈现授权码模式在移动端的完整流程,标明 redirect_uri 的注册与匹配规则,让开发者一眼看出为何某些回调地址会导致「无法完成登录」的报错,从而快速定位配置问题。

🔐

API 权限委托设计

SaaS 平台架构师在设计「用户授权第三方应用访问其数据」的功能时,需要区分客户端凭证模式(Client Credentials)与授权码模式的使用边界。本工具通过对比流程图,直观展示前者适用于服务器到服务器的无用户场景,后者适用于有用户参与的授权场景,帮助架构师在微服务间调用与用户数据委托两种需求中做出正确的 grant type 选择,避免权限过度暴露。

📄

OAuth 安全审计

安全工程师在审计已有 OAuth 2.0 实现时,需要逐一核对令牌端点是否验证 client_secret、授权码是否一次性使用、刷新令牌是否绑定到特定客户端。本工具将四种 grant 流程拆解为可交互的步骤图,审计人员可以对照每一步的 RFC 规范描述,快速标记出实现中缺失的校验环节,例如「授权码模式缺少 PKCE 扩展」或「隐式模式未限制 response_type」,从而生成精准的修复清单。

🔄

刷新令牌策略制定

后端开发者在设计长期有效的 API 访问时,需要决定刷新令牌(Refresh Token)的过期时间与轮换策略。本工具展示授权码模式中刷新令牌的完整生命周期流程,并标注「一次使用」与「轮换」两种策略在序列图中的差异,帮助开发者直观理解为何轮换策略能降低令牌泄露的持续风险,以及如何结合过期时间设置实现自动续期与安全退出的平衡。

对比矩阵本工具 vs 竞品 vs 传统方法

维度本工具 (oauth-flow.tl654.com)Auth0 在线调试器手工搭建 (Spring Security / Passport.js)
数据隐私纯浏览器,流程图和令牌均在本地生成,无任何数据上传令牌和授权码在 Auth0 服务器上生成和验证令牌在自建服务器上生成,需自行保障服务器安全
处理速度毫秒级,流程图与令牌生成完全在浏览器内完成依赖网络请求,通常 1-3 秒依赖服务器响应与网络延迟,通常 0.5-5 秒
离线可用完全离线,无需网络连接必须联网必须联网,且需运行后端服务
上手成本零配置,打开页面即可选择 Grant 类型并生成流程需要注册 Auth0 账号并创建应用需搭建开发环境、配置依赖库、编写路由代码,数小时到数天
Grant 类型覆盖四种核心 Grant(Authorization Code、Implicit、Client Credentials、Resource Owner Password Credentials)支持所有 Grant,并额外支持 PKCE、Device Code 等扩展取决于框架,通常需手动实现或引入第三方库
令牌可视化流程图实时高亮令牌流转路径,附带令牌结构解析返回 JSON 格式令牌,需自行理解字段含义返回原始令牌,需配合文档或工具解码
适用场景学习 OAuth 2.0 流程、快速验证 Grant 逻辑、教学演示生产环境集成测试、OAuth 服务端调试生产级应用开发、自定义安全策略

使用指南

上手步骤 · 输入输出 · 避坑提示

输入输出示例7 个典型场景,覆盖常规、边界与易错

输入输出说明
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https://client.example.com/cb&client_id=s6BhdRkqt3显示授权码流程(Authorization Code Grant)的完整步骤图:1. 用户浏览器访问授权端点 → 2. 用户登录并授权 → 3. 授权服务器返回授权码 → 4. 客户端用授权码向令牌端点请求 → 5. 返回 access_token(+refresh_token)。典型场景:最常用的 OAuth 2.0 流程,用于 Web 应用
grant_type=client_credentials&client_id=s6BhdRkqt3&client_secret=secret123显示客户端凭证流程(Client Credentials Grant)的步骤图:1. 客户端直接向令牌端点请求 → 2. 返回 access_token(无 refresh_token)。典型场景:服务器到服务器的 API 调用,无用户参与
grant_type=password&username=johndoe&password=A3ddj3w&client_id=s6BhdRkqt3显示密码流程(Resource Owner Password Credentials Grant)的步骤图:1. 用户向客户端提供用户名密码 → 2. 客户端向令牌端点请求 → 3. 返回 access_token(+refresh_token)。典型场景:受信任的客户端(如官方 App)直接收集凭据
grant_type=implicit&response_type=token&client_id=s6BhdRkqt3&redirect_uri=https://client.example.com/cb&scope=openid%20profile显示隐式流程(Implicit Grant)的步骤图:1. 用户浏览器访问授权端点 → 2. 用户登录并授权 → 3. 授权服务器直接返回 access_token(在 URL 片段中)。边界 case:已不推荐使用(安全风险),仅用于纯前端应用
grant_type=authorization_code&code=invalid_code_12345&redirect_uri=https://client.example.com/cb&client_id=s6BhdRkqt3错误响应:{"error":"invalid_grant","error_description":"The provided authorization grant is invalid, expired, or revoked."}易错 case:授权码过期或伪造,新手常忽略有效期
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA&client_id=s6BhdRkqt3&client_secret=secret123显示刷新令牌流程:1. 客户端用 refresh_token 向令牌端点请求 → 2. 返回新的 access_token(+新的 refresh_token)。边界 case:access_token 过期后,用 refresh_token 无感续期
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https://attacker.com/cb&client_id=s6BhdRkqt3错误响应:{"error":"invalid_grant","error_description":"The redirect_uri does not match the registered redirect_uri."}易错 case:redirect_uri 不匹配,新手常忽略注册时的精确匹配

常见错误对照8 个常踩的坑 · 错误 → 修复

1. 混淆授权码模式与隐式模式

错误
在纯前端 SPA 中使用授权码模式(Authorization Code),且无后端参与,直接暴露 client_secret。
修复
SPA 使用授权码模式 + PKCE(Proof Key for Code Exchange),或使用隐式模式(Implicit Grant,已不推荐)。

授权码模式要求 client_secret 保密,纯前端无法安全存储;PKCE 用 code_verifier 替代 secret,适合无后端场景。

2. 客户端凭证模式中传入用户身份信息

错误
请求参数包含 `scope=user_info` 或 `login_hint=user@example.com`。
修复
客户端凭证模式只传 `grant_type=client_credentials` 和 `scope=api_read`(仅限应用级别权限)。

此模式用于机器对机器通信,不涉及具体用户;传入用户信息会被服务端忽略或报错。

3. 授权码模式中 state 参数缺失或固定值

错误
`state=123456`(固定值)或完全省略 state 参数。
修复
`state=random_generated_nonce`(每次请求生成唯一随机值,并在回调中验证)。

state 用于防御 CSRF 攻击;固定值或缺失导致攻击者可伪造回调劫持用户会话。

4. 隐式模式中 access_token 暴露在 URL 片段后未及时清除

错误
用户复制包含 `#access_token=...` 的 URL 分享给他人,或浏览器历史记录中保留该 URL。
修复
使用后立即调用 `window.location.hash = ''` 清除片段,并告知用户不分享回调 URL。

URL 片段中的 token 会被浏览器历史、Referer 头泄露;隐式模式本身安全性较低,需额外防护。

5. 密码模式(Resource Owner Password Credentials)中明文存储密码

错误
客户端将用户密码以 Base64 编码后直接存入本地 localStorage。
修复
密码仅用于单次 token 请求,获取 access_token 后立即丢弃密码,不持久化。

密码模式要求客户端高度可信;持久化密码违背最小权限原则,一旦客户端被攻破,所有凭据泄露。

6. 刷新令牌(refresh_token)使用不当

错误
每次刷新都使用同一个 refresh_token,且不轮换(rotation)。
修复
使用 refresh_token 获取新 access_token 后,丢弃旧 refresh_token,使用新返回的 refresh_token。

RFC 6749 推荐 refresh_token 轮换;固定 token 被窃取后可无限期使用,轮换可限制泄露窗口。

7. 重定向 URI(redirect_uri)未严格校验

错误
注册时填写 `https://example.com/callback`,但实际请求中传入 `https://evil.com/callback`。
修复
服务端必须精确匹配注册的 redirect_uri(包括路径、端口、协议),拒绝任何未注册的 URI。

开放重定向允许攻击者截获授权码或 token;严格校验是 OAuth 2.0 安全基础之一。

8. 混淆 grant_type 与 response_type

错误
授权码模式请求中写 `grant_type=authorization_code` 但实际是第一步获取 code 的请求。
修复
第一步授权请求用 `response_type=code`;第二步 token 请求用 `grant_type=authorization_code`。

grant_type 用于 token 端点(第二步),response_type 用于授权端点(第一步);混用导致协议错误。

工作原理

公式推导 · 流程图解 · 依据出处

核心公式

授权码流程:code → token(通过 client_id + client_secret + redirect_uri 交换)

变量说明

  • code — 授权码,临时凭证,有效期短
  • client_id — 客户端标识,注册时分配
  • client_secret — 客户端密钥,仅后端持有
  • redirect_uri — 回调地址,必须与注册一致
  • access_token — 访问令牌,用于调用资源 API

示例

用户授权后,授权服务器返回 code=abc123。客户端用 POST 请求向 /token 端点发送:client_id=myapp&client_secret=secret&code=abc123&redirect_uri=https://example.com/callback&grant_type=authorization_code。服务器验证通过后返回 access_token=eyJhbGciOiJIUzI1NiIs...(JWT 格式),有效期 3600 秒。

适用范围

适用于有后端服务器的 Web 应用(机密客户端)。纯前端 SPA 或移动端无法安全保存 client_secret,应使用 PKCE 扩展流程。基于 IETF RFC 6749 标准。

原理图

OAuth 2.0 授权码流程(Authorization Code Grant)纯浏览器端实现 — 所有步骤在用户浏览器中完成用户点击“登录”重定向到授权页用户授权(同意)获取授权码展示访问令牌令牌可用于 API 调用刷新令牌(可选)令牌过期后重新获取
用户操作 / 重定向 浏览器内处理(授权码交换) 令牌生命周期管理

开发者集成

3 种主流语言 · 复制即用

import requests
from urllib.parse import urlencode, urljoin

# 授权码模式(Authorization Code)客户端示例
CLIENT_ID = "your_client_id"
CLIENT_SECRET = "your_client_secret"
REDIRECT_URI = "https://your-app.com/callback"
AUTH_ENDPOINT = "https://provider.com/oauth2/authorize"
TOKEN_ENDPOINT = "https://provider.com/oauth2/token"

# 1. 构造授权请求 URL(用户浏览器跳转)
params = {
    "response_type": "code",
    "client_id": CLIENT_ID,
    "redirect_uri": REDIRECT_URI,
    "scope": "openid profile",
    "state": "random_state_string"  # 防 CSRF
}
auth_url = f"{AUTH_ENDPOINT}?{urlencode(params)}"
print(f"Redirect user to: {auth_url}")

# 2. 回调后,用授权码交换令牌(服务端执行)
authorization_code = "code_from_callback"  # 从 query 参数获取

token_data = {
    "grant_type": "authorization_code",
    "code": authorization_code,
    "redirect_uri": REDIRECT_URI,
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET
}
response = requests.post(TOKEN_ENDPOINT, data=token_data)
if response.status_code == 200:
    token = response.json()
    print(f"Access token: {token['access_token']}")
    print(f"Refresh token: {token.get('refresh_token')}")
else:
    print(f"Token exchange failed: {response.text}")
package main

import (
	"context"
	"fmt"
	"golang.org/x/oauth2"
)

func main() {
	// 客户端凭证模式(Client Credentials)—— 服务到服务认证
	conf := &oauth2.Config{
		ClientID:     "your_client_id",
		ClientSecret: "your_client_secret",
		Scopes:       []string{"api:read"},
		Endpoint: oauth2.Endpoint{
			TokenURL: "https://provider.com/oauth2/token",
		},
	}

	// 直接获取令牌,无需用户交互
	token, err := conf.TokenSource(context.Background(), &oauth2.Token{}).Token()
	if err != nil {
		fmt.Printf("Failed to get token: %v\n", err)
		return
	}

	fmt.Printf("Access token: %s\n", token.AccessToken)
	fmt.Printf("Token type: %s\n", token.TokenType)
	fmt.Printf("Expires in: %v\n", token.Expiry)

	// 使用令牌访问受保护资源
	client := conf.Client(context.Background(), token)
	resp, err := client.Get("https://api.provider.com/v1/resource")
	if err != nil {
		fmt.Printf("API call failed: %v\n", err)
		return
	}
	defer resp.Body.Close()
	fmt.Printf("API response status: %s\n", resp.Status)
}
// 隐式模式(Implicit)—— 纯前端 SPA 示例(已不推荐,仅作演示)
// 实际生产应使用授权码 + PKCE

// 1. 生成 PKCE 验证码和挑战(推荐替代隐式模式)
function generatePKCE() {
  const verifier = Array.from(crypto.getRandomValues(new Uint8Array(32)))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
  
  // 生成 SHA-256 挑战
  return crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))
    .then(hash => {
      const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=+$/, '');
      return { verifier, challenge };
    });
}

// 2. 构造授权请求(PKCE 增强的授权码模式)
async function startAuth() {
  const { verifier, challenge } = await generatePKCE();
  
  // 保存验证码到 localStorage(回调时使用)
  localStorage.setItem('pkce_verifier', verifier);
  
  const params = new URLSearchParams({
    response_type: 'code',
    client_id: 'your_client_id',
    redirect_uri: 'https://your-app.com/callback',
    code_challenge: challenge,
    code_challenge_method: 'S256',
    state: crypto.randomUUID(),
    scope: 'openid profile'
  });
  
  window.location.href = `https://provider.com/oauth2/authorize?${params}`;
}

// 3. 回调处理:用授权码 + PKCE 验证码换令牌
async function exchangeCode(code) {
  const verifier = localStorage.getItem('pkce_verifier');
  localStorage.removeItem('pkce_verifier');
  
  const response = await fetch('https://provider.com/oauth2/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code,
      redirect_uri: 'https://your-app.com/callback',
      client_id: 'your_client_id',
      code_verifier: verifier
    })
  });
  
  if (!response.ok) throw new Error('Token exchange failed');
  const token = await response.json();
  console.log('Access token:', token.access_token);
  return token;
}

常见问题

8 个高频疑问

这个 OAuth 2.0 流程图和 RFC 文档里的图一样吗?
本工具按 RFC 6749 核心规范绘制四种 grant 流程(授权码、隐式、密码凭证、客户端凭证),关键交互节点和 token 传递方向与规范一致。但为了可读性,省略了部分可选细节(如 refresh_token 的单独请求、scope 协商的具体参数)。如果做严格合规审查,建议对照 RFC 6749 Section 4.1-4.4 原始时序图。
为什么我按流程图实现了授权码模式,但实际跑不通?
常见原因有三:一是 redirect_uri 必须在注册时精确匹配(包括 protocol、端口、路径),流程图里只画了『重定向』这一步,没标出校验逻辑;二是 state 参数防 CSRF 图中未强调,但许多严格的服务端(如 Google、GitHub)会强制校验;三是 token endpoint 的 client authentication 方式(basic / post body)不同服务要求不同。建议结合具体 IdP 的文档对比差异。
隐式模式和授权码模式现在还能用吗?哪个更推荐?
授权码模式仍是推荐方案,RFC 6749 明确支持。隐式模式在 OAuth 2.1 草案中已被标记为不推荐(因为 access_token 直接暴露在 URL fragment 中,存在泄漏风险),主流 IdP 如 Google 已逐步停止新应用注册隐式模式。如果做新系统,直接用授权码模式 + PKCE(RFC 7636),安全级别更高,且兼容 SPA 和原生 App。
流程图里没有 PKCE 扩展,纯授权码模式在移动端安全吗?
不绝对安全。纯授权码模式依赖 client_secret 验证,但在纯前端(移动 App、SPA)中 client_secret 无法保密,攻击者可以反编译或抓包获取。本工具流程图展示的是基础 RFC 6749 流程,未包含 PKCE 扩展。实际移动端开发建议在授权码请求中加上 code_challenge 和 code_challenge_method 参数,服务端需支持 PKCE 验证。
密码凭证模式(Resource Owner Password Credentials)现在还能用吗?
RFC 6749 允许,但强烈不推荐用于第三方应用。OAuth 2.0 Security BCP(RFC 9700)明确建议:仅当『高度信任』且『无其他可行方案』时才用。实际场景中,密码模式通常被用于第一方应用(如自家 App 登录自家 API),或传统迁移场景。如果做新应用,优先选授权码 + PKCE。本工具保留该流程主要是为了对比学习。
我输入参数后流程图不会动,是 bug 还是本来就这样?
本工具是静态示意图,不是交互式模拟器。流程图展示的是每个 grant 的标准交互步骤顺序,点击或输入参数不会触发动画或生成动态序列。如果需要交互式调试(如模拟 token 请求、查看实际 HTTP 报文),可以配合 Postman 或 OAuth 2.0 Playground(如 Google 或 Okta 的在线工具)使用。
为什么有些第三方登录(如微信、微博)的流程和这个图不完全一样?
主流 IdP 通常基于授权码模式做定制扩展。例如微信添加了『静默授权』和『显式授权』两种 scope 模式,且 access_token 有效期较短(2 小时),需要额外用 refresh_token 续期;微博的 OAuth 2.0 实现中,用户授权后直接返回 access_token,但实际的 token 交换还是走授权码模式。差异多体现在 scope 定义、token 格式(JWT vs opaque)和续期策略上,核心交互骨架相同。
这个工具能生成 OAuth 2.0 的代码示例吗?比如 Python 或 JavaScript 的?
不能。本工具只提供流程图和文字解释,不生成任何语言的代码片段。如果需要代码示例,可以参考 RFC 6749 附录(示例 HTTP 请求/响应),或各语言的 OAuth 2.0 客户端库文档(如 Python 的 requests-oauthlib、Node.js 的 simple-oauth2)。流程图中的『Client』和『Authorization Server』节点需要自行实现或使用第三方 SDK。
选择 打开 +新窗口 esc关闭