3.5 从密码到token,一个有关授权的故事

从把用户密码交给每个第三方应用走到授权码、PKCE、范围和期限约束的令牌访问,沿 OAuth 授权边界诊断重放与泄露。

学习目标

  • 能沿授权请求、用户同意、授权码回调、后端换令牌和资源访问追踪一次 OAuth 授权码流程
  • 能解释授权码、PKCE、redirect URI、scope、令牌期限和资源服务器各自承担的安全边界
  • 能在正常、边界和故障场景中回答:授权码被截获、重放或 redirect URI 不匹配时,哪一步应拒绝

3.5 从密码到token,一个有关授权的故事

本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 3.5 从密码到token,一个有关授权的故事。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。

先想象你把房间钥匙交给一个代办人,让他替你取一次文件。更安全的做法是:你在门卫处确认代办人的身份和任务范围,门卫给他一张只能用一次、只能去指定窗口的取件单;代办人拿取件单换一张短期通行证,而不是拿到你的总钥匙。

这一章解决的是“第三方应用怎样代表用户获得有限访问,而不接触用户密码”。如果客户端直接收密码,密码会在更多地方复制、缓存和泄露;如果只发一个没有范围和期限的通行证,窃取后又能长期横向访问。授权码流程把登录、同意、交换和资源访问拆开,让每个边界都能被拒绝和审计。

OAuth 授权码链:密码留在授权服务器code 绑定客户端与回调,token 再按范围和受众访问资源1发起授权client + scope授权证据2用户同意登录 + grant授权证据3返回授权码state + redirect授权证据4后端换令牌code + PKCE凭据交换5携令牌访问aud + scope资源证据授权码不是资源令牌:交换时仍要验证 redirect URI 与 PKCE
专属图示:把用户同意、一次性 code、令牌交换和资源验证拆成边界。

三个会让授权流程失真的陷阱

五个目录节点到授权证据

3.5 从密码到token,一个有关授权的故事

总合同是:客户端发起带 state、scope、redirect URI 和 PKCE challenge 的授权请求;授权服务器让资源所有者登录并同意;回调只带一次性授权码;后端以同一客户端和 verifier 换取有范围/期限的令牌;资源服务器验证令牌后才返回受保护资源。

我把密码献给你

描述的是凭据边界错误。客户端不应知道用户密码,也不应模拟授权服务器登录;用户登录、同意、撤销和多因素校验都留在授权服务器一侧。

token

不是万能钥匙。检查它的受众、签发者、范围、过期时间和传输方式;访问令牌泄露后的影响由短期限、最小 scope、绑定或撤销策略限制。

授权码 token

把“用户同意”与“资源访问”分成两次交付。授权码本身不应直接调用资源 API;交换时必须再次校验客户端身份、redirect URI 和 PKCE verifier。

OAuth 授权码链:密码留在授权服务器code 绑定客户端与回调,token 再按范围和受众访问资源1发起授权client + scope授权证据2用户同意登录 + grant授权证据3返回授权码state + redirect授权证据4后端换令牌code + PKCE凭据交换5携令牌访问aud + scope资源证据授权码不是资源令牌:交换时仍要验证 redirect URI 与 PKCE
专属图示:把用户同意、一次性 code、令牌交换和资源验证拆成边界。

后记

提醒我们,令牌格式不是安全策略的全部。系统还要保护 state、回调、浏览器存储、日志、刷新令牌和撤销接口;每个应用只获得完成任务所需的最小权限。

最小授权码合同

authorize(client_id, redirect_uri, scope, state, code_challenge)
user_consents()
code = callback(redirect_uri, state)
token = exchange(code, client_id, redirect_uri, code_verifier)
resource = api_call(token, required_scope)

合同要求授权码与原始请求绑定,交换时验证 state、客户端、回调地址和 PKCE;令牌请求与资源请求分开记录。实现中不要把 code、access token 或 refresh token 写入普通日志,也不要把开放重定向当成“方便配置”。

五步复核一次授权流程

分步1 / 5

1. 发起授权并固定绑定值

记录客户端 ID、精确 redirect URI、scope、state 和 code challenge。基线只请求一个资源范围;边界场景改动回调地址,预期是授权服务器拒绝未注册地址。

OAuth 授权码链:密码留在授权服务器code 绑定客户端与回调,token 再按范围和受众访问资源1发起授权client + scope授权证据2用户同意登录 + grant授权证据3返回授权码state + redirect授权证据4后端换令牌code + PKCE凭据交换5携令牌访问aud + scope资源证据授权码不是资源令牌:交换时仍要验证 redirect URI 与 PKCE
专属图示:把用户同意、一次性 code、令牌交换和资源验证拆成边界。

Lab

OAuth 授权绑定实验

只改变回调或授权码绑定,观察令牌是否签发以及资源服务器是否可见。

回调、state 和 PKCE 匹配,按最小 scope 访问

consent → one-time code → verifier match → token(scope=read) → API 200

判定

accept:密码不离开授权服务器,资源范围可解释

当前样本:正常授权;保存 state、redirect URI、code 状态、verifier、scope 和资源响应。

正常、边界与故障证据矩阵

OAuth 证据矩阵:每次交付都要回到绑定关系正常样本看授权闭环,边界样本看最小权限,故障样本看拒绝观察项正常边界故障绑定client/回调一致scope 变化开放回调回调state 匹配用户拒绝code 重放交换PKCE 匹配code 过期verifier 错资源aud/scope 对临近过期受众不符先保存 state、绑定摘要、code 状态和令牌声明,再判定资源是否可见
专属图示:把回调绑定、授权码生命周期、PKCE 和资源策略逐层对齐。
样本只改变的变量预期判定必存证据
正常client、回调、state、PKCE 和 scope 匹配一次换 token,资源按范围返回绑定摘要、授权码状态、令牌声明
边界scope 扩大、令牌临近过期或回调变化要求重新同意/刷新或拒绝同意内容、过期时间、拒绝原因
故障code 重放、verifier 错或 token 受众错交换/资源访问失败,不泄露资源首个失败检查、请求 ID、响应状态

故障诊断:先找哪条绑定断了

  1. 授权请求侧:查 client、预注册回调、scope、state 和 challenge;开放重定向应直接判为配置错误。
  2. 回调侧:查 state、错误参数、授权码生命周期和地址清理;回调成功不等于令牌已签发。
  3. 交换侧:查 client 认证、redirect URI、code verifier、一次性状态和过期时间;只要一个绑定不符就拒绝。
  4. 资源侧:查令牌签发者、受众、scope、期限、撤销和传输;资源服务器不能只解码而不验证。

如果用户同意后没有 token,先分离回调失败与交换失败;如果 code 能被第二次兑换,检查一次性状态和并发处理;如果 token 有效但资源被拒绝,查受众/范围/资源服务器策略。每次只改变一个绑定并重放基线。

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

我把密码献给你

第三方应用直接收集用户密码的危险做法,授权码流程要消除它。

token

代表授权结果、带范围和期限等约束的资源访问凭据。

授权码 token

先交付一次性授权码,再由后端绑定客户端和 PKCE 换取令牌的流程。

后记

把授权回收到凭据、范围、期限、受众、撤销和审计共同构成的工程边界。

练习

练习

问题 1: 为什么授权码不能直接当访问令牌调用资源 API?

问题 2: 授权码被截获时,state 和 PKCE 分别解决什么问题?

问题 3: 修改本页“OAuth 授权绑定实验”的故障场景,使它显示一次 redirect URI 不匹配和一次 code 重放,并说明重置后应恢复哪些状态。

资料与写作方式声明

本章以码农翻身权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

本页小结

  • 授权码流程让授权服务器持有密码,客户端只接收一次性 code。
  • redirect URI、state 和 PKCE 把浏览器回调绑定到原始请求。
  • token 需要范围、受众、期限、传输和撤销边界。
  • 资源服务器必须验证令牌,而不是只解码其内容。

读完后的自测问题是:当授权码被截获或 redirect URI 被替换时,你能否指出交换端应检查的绑定、拒绝结果和必须保留的审计证据?

讨论

评论区加载中…