OIDC认证与Secrets轮换联动实践 - 第3节
OIDC 认证流程:三种模式的深度理解
好,咱们直接切入主题。OIDC 的认证流程,说白了就是客户端怎么去身份提供商(OP)那里拿身份证的过程。
这三种流程——授权码流程(Authorization Code Flow)、隐式流程(Implicit Flow)和混合流程(Hybrid Flow),核心区别就在于令牌怎么交到你手上。
一句话总结:
- 授权码流程——后端做中间人,最安全,我一般默认选它。
- 隐式流程——前端直接拿 access ,适合纯 SPA,但后来被 PKCE 替代了。
- 混合流程——前后端分着拿,高级用法,遇到特殊业务才用。
为什么要有三种流程?
回想一下 OAuth 2.0 的几个 grant type,OIDC 直接沿用了它们。授权码流程对应 authorization_code,隐式流程对应 implicit,混合流程则是两者的组合。你想想看,不同的客户端能力不一样——有的有后端服务器(比如 Web 应用),有的只有浏览器(比如纯静态页面),还有的既需要即时拿到 ID 又想在后台换 refresh 。所以协议得给够选择。
我遇到过不少开发者,上来就说“我用隐式流程,简单”。嗯,后来出了安全漏洞,又得改。所以咱们得把每个流程的坑摸清楚。
三种流程核心对比
| 维度 | 授权码流程 | 隐式流程 | 混合流程 |
|---|---|---|---|
| 令牌传递方式 | 后端通过 code 换取,不暴露给浏览器 | 浏览器 get 请求直接拿到 fragment 中的令牌 | 前端拿部分令牌,后端通过 code 拿其余 |
| client_secret 使用 | 必需(后端凭证) | 不使用(全在浏览器) | 按需(后端环节使用) |
| access 泄露风险 | 低 | 高(URL 历史、referer 泄漏) | 中等 |
| 支持 refresh | 支持 | 不支持(不允许) | 支持(需后端配合) |
| 适用场景 | 服务端渲染 Web 应用、原生应用 | 纯前端 SPA(已被 PKCE 授权码 + code_challenge 替代) | 需要前端快速获取 id_ 又希望后端管控 access_ 的场景 |
授权码流程(Authorization Code Flow)
这是 OIDC 最正统的流程,也是最安全的。核心思想:认证过程在 OP 侧完成,最终只会给客户端一个临时的 code,真正的令牌由后端通过 client_secret + code 换取,全程不经过浏览器。
完整步骤(以 Web 应用为例)
- 用户点击“登录”,浏览器被重定向到 OP 的授权端点,带上 client_id、redirect_uri、response_type=code 和 scope=openid。
- OP 验证用户身份(比如弹出登录页面),用户确认授权。
- OP 将浏览器重定向回你的 redirect_uri,并在 URL query 参数中附上一个 authorization code。
- 你的后端服务器接收到这个 code,直接通过后台通道(HTTP POST)向 OP 的 端点发起请求,附上 client_id、client_secret、code、redirect_uri。
- OP 验证 client_secret 和 code 合法后,返回 id_ + access_ + refresh_(如果申请了)。
- 后端验证 id_ 签名后,建立本地 session,然后响应给浏览器(可以设置 cookie 或者返回 session_id)。
我习惯的做法:把 code 交换令牌的 endpoint 专门限制为只允许服务端 IP 访问,并且强制使用 HTTPS。client_secret 不能硬编码到前端代码里,否则拿了 code 白拿。
避坑指南
我曾经在一个项目中遇到过一个奇葩问题:redirect_uri 在 OP 配置的是 https://example.com/callback,但测试时因为本地开发用了 http://localhost:8080,结果 code 根本发不回来。OIDC 规定 redirect_uri 必须严格匹配,包括端口和协议。所以别忘了在 OP 管理后台把多个合法 redirect_uri 都注册上,或者开发环境用 https 代理。
另外,code 是一次性的,而且有有效期(通常 5-10 分钟)。收到 code 后立即发起交换请求,不要存着回头再用。如果重放攻击,OP 应该拒绝重复的 code。
隐式流程(Implicit Flow)
这个流程是 OAuth 2.0 时期留给纯浏览器应用的解决方案。特点:不经过后端,access 直接通过 URL fragment 回到前端。
步骤
- 浏览器重定向到 OP 授权端点,response_type=id_ 或者 id_,具体看你想要什么。
- OP 验证用户后,直接重定向回 redirect_uri,在 URL fragment(# 后面)附上 id_ 和/或 access_。
- 前端 JavaScript 读取 window.location.hash,提取令牌,然后删掉 hash(避免泄露到历史记录里)。
注意:隐式流程无法使用 client_secret,因为 secret 暴露在浏览器里毫无意义。所以所有安全性都依赖 redirect_uri 的严格校验和 PKCE(但隐式流程原生不支持 code_challenge)。现在主流安全建议已经不再推荐隐式流程了,除非你不得不兼容非常老的 OAuth 2.0 实现。
实操中,access 会暴露在浏览器历史、referer 头里,甚至被中间人截获。我曾经测试过一个国产 SSO 平台,发现他们把 暴露在页面底部的一个隐藏 input 里——这等于开门揖盗。
混合流程(Hybrid Flow)
混合流程有点像“既要又要”。它让你可以在前端直接拿到 id_(用于快速判断用户身份),同时通过 code 在后端安全地换取 access_ 和 refresh_。
触发方式
将 response_type 参数设置为混合值,比如 code id_、code 或者 code id_ 。注意,OIDC 规定混合流程必须包含 code(因为这是授权码流程的基础)。
步骤示意
- 浏览器带着 response_type=code id_ 去授权端点。
- OP 验证后,一次重定向回来,URL 里同时包含 fragment 中的 id_ 和 query 中的 code。
- 前端立即解析 id_,可以立刻展示用户信息(比如用户名、头像)。但注意,此时还不能信任 id_,因为它没有经过后端验证签名(前端没有 client_secret)。
- 后端收到 code 后,通过 端点换回 access_ 和 refresh_,并验证 id_ 的一致性(比如 nonce 匹配)。
- 后端验证通过后,把真正的用户身份写入 session,前端拿到 session 后再展示敏感数据。
我在设计一个需要即时展示用户昵称、但后续 API 请求又需要高安全凭证的系统时,用了混合流程。前端拿到 id_ 直接显示“欢迎回来,小明”,后端则走 code 换 的完整路径。这样用户体验和安全性都兼顾了。但要注意,id_ 中的 claims 在后端验证之前不能用于任何授权决策。
SVG: OIDC 三种流程对比结构图
安全关键点与个人经验
核心原则:不要在前端暴露任何需要 client_secret 保护的令牌。
我曾经参与审计一个金融级应用,发现开发团队把授权码流程的 client_secret 写在了前端 JavaScript 里,美其名曰“方便调试”。结果扫描工具直接报高危漏洞。记住:client_secret 等同于后端密码,只能放在服务器环境变量或密钥管理服务里。
各流程的选择建议
- 面向用户的 Web 应用(有后端):无脑选授权码流程 + PKCE。即使你没有 client_secret(比如原生应用),也请使用 PKCE 扩展。我的默认配置就是 authorization_code + S256 code_challenge。
- 纯前端 SPA 但需要 refresh :用授权码流程 + PKCE,不要被“需要后端”吓到。很多 OIDC 库(比如 oidc-client-ts)都支持在浏览器端执行 authorization code exchange,但缺陷是 refresh 仍会暴露在浏览器内存中。不过比隐式流程好。
- 混合流程只在有明确需求时用:比如你需要在页面渲染前就拿到 id_ 做路由守卫,同时又需要后端安全地换取 access_。注意验证 nonce 防止重放,并确保前端拿到的 id_ 不包含敏感信息(sub 和 email 就够了)。
nonce 的作用
三种流程都需要检查 nonce。nonce 是个一次性的随机字符串,由客户端生成并传入授权请求,OP 原样放进 id_ 里。客户端验证 id_ 时必须确认 nonce 一致,防止令牌被偷用。我习惯每次认证前用 crypto.randomUUID() 生成一个 nonce,存到 sessionStorage 里。
令牌轮换与联动
这是后面几章会深入的内容。不过这里先提一句:授权码流程天然支持 refresh ,而 refresh 的轮换策略(Rotation)和回撤(Revocation)是安全联动的关键。隐式流程不支持 refresh ,所以一旦 access 过期,用户必须重新登录——体验很差。混合流程虽然支持,但需要后端参与 refresh 过程,不能全在浏览器里做。
嗯,到这里三种流程的基本逻辑和实战要点就覆盖完了。你心里应该已经有数了:选授权码流程做主力,把隐式流程当历史知识了解,混合流程作为高阶工具备用。