Blog·Studio
文章系列日历归档关于搜索
Blog·Studio

一个记录思考、笔记与作品的技术博客。

Connect

© 2026 · Blog Studio

鄂ICP备19019526号

crafted with care

stay curious ✦

  1. 文章
  2. ›AI 应用委托授权工程 2026:代用户操作的凭证边界

Index

  • 一、问题的提出:代用户操作把授权变成了运行时问题
  • 1.1 从"读自己的库"到"动别人的系统"
  • 1.2 三个被反复混淆的"授权"
  • 1.3 为什么现有 AI 工程讨论几乎不谈这一层
  • 二、委托授权的四层边界模型
  • 2.1 L1 主体边界:同意的来源与绑定
  • 2.2 L2 资源边界:令牌的受众与 confused deputy
  • 2.3 L3 动作边界:同意的范围
  • 2.4 L4 时间边界:同意的有效期与收回
  • 三、授权码流与 PKCE:把"同意"从模型手里拿回来
  • 3.1 一条不可协商的规则
  • 3.2 MCP 授权规范给出的完整流程骨架
  • 3.3 授权请求里必须带的东西
  • 四、受众绑定与 confused deputy:为什么不能把令牌转发下去
  • 4.1 两条独立的失效路径
  • 4.2 "只接受自己的令牌"这句话的工程含义
  • 4.3 audience 校验的具体做法
  • 4.4 错误码语义也属于协议
  • 五、委托 vs 冒充:RFC 8693 的两套语义与它的边界
  • 5.1 两种语义
  • 5.2 协议怎么表达
  • 5.3 token exchange 的现实约束
  • 六、凭证的自我保护:DPoP、短期令牌与刷新令牌轮换
  • 6.1 AI 应用的令牌暴露面
  • 6.2 DPoP:把 bearer 变成 proof-of-possession
  • 6.3 刷新令牌轮换:截断重放窗口
  • 七、撤销与断权:让"用户收回授权"成为协议动作
  • 7.1 三种撤销语义必须分清
  • 7.2 把用户的自然语言映射到协议动作
  • 7.3 撤销必须可验证,而不是只能相信
  • 八、从规范到代码:自建连接器的十二项检查
  • 九、局限、争议与未公开验证的猜想
  • 十、小结
  • 参考文献

AI 应用委托授权工程 2026:代用户操作的凭证边界

当 AI 应用开始替用户去操作第三方系统——发邮件、改工单、调支付——真正的工程难题不再是模型够不够聪明,而是这份凭证到底代表谁、授权给谁、覆盖哪一次动作。本文以 RFC 9700、9449、8693、8707、9728 与 MCP 2025-06-18 授权规范为骨架,给出一套从授权码流、受众绑定、委托语义到撤销断权的完整落地方法与十二项检查清单。

2026年10月27日·约 78 分钟阅读·23,307 字·6 次阅读·博主
#智能体与 AI 应用开发
AI 应用委托授权工程 2026:代用户操作的凭证边界

Index

  • 一、问题的提出:代用户操作把授权变成了运行时问题
  • 1.1 从"读自己的库"到"动别人的系统"
  • 1.2 三个被反复混淆的"授权"
  • 1.3 为什么现有 AI 工程讨论几乎不谈这一层
  • 二、委托授权的四层边界模型
  • 2.1 L1 主体边界:同意的来源与绑定
  • 2.2 L2 资源边界:令牌的受众与 confused deputy
  • 2.3 L3 动作边界:同意的范围
  • 2.4 L4 时间边界:同意的有效期与收回
  • 三、授权码流与 PKCE:把"同意"从模型手里拿回来
  • 3.1 一条不可协商的规则
  • 3.2 MCP 授权规范给出的完整流程骨架
  • 3.3 授权请求里必须带的东西
  • 四、受众绑定与 confused deputy:为什么不能把令牌转发下去
  • 4.1 两条独立的失效路径
  • 4.2 "只接受自己的令牌"这句话的工程含义
  • 4.3 audience 校验的具体做法
  • 4.4 错误码语义也属于协议
  • 五、委托 vs 冒充:RFC 8693 的两套语义与它的边界
  • 5.1 两种语义
  • 5.2 协议怎么表达
  • 5.3 token exchange 的现实约束
  • 六、凭证的自我保护:DPoP、短期令牌与刷新令牌轮换
  • 6.1 AI 应用的令牌暴露面
  • 6.2 DPoP:把 bearer 变成 proof-of-possession
  • 6.3 刷新令牌轮换:截断重放窗口
  • 七、撤销与断权:让"用户收回授权"成为协议动作
  • 7.1 三种撤销语义必须分清
  • 7.2 把用户的自然语言映射到协议动作
  • 7.3 撤销必须可验证,而不是只能相信
  • 八、从规范到代码:自建连接器的十二项检查
  • 九、局限、争议与未公开验证的猜想
  • 十、小结
  • 参考文献

AI 应用委托授权工程 2026:代用户操作的凭证边界

导语:当 AI 应用开始替用户去操作第三方系统——发邮件、改工单、调支付、退款、发消息——真正的工程难题不再是"模型够不够聪明",而是"这份凭证到底代表谁、授权给谁、覆盖哪一次动作"。本文以 RFC 9700 / 9449 / 8693 / 8707 / 9728 与 MCP 2025-06-18 授权规范为骨架,给出一套从授权码流、受众绑定、委托语义到撤销断权的完整落地方法。

过去两年关于 AI 应用工程的讨论,绝大部分集中在两个方向:一类是把 Agent 内核做扎实——规划、记忆、工具调用、多智能体编排;另一类是把某个垂直场景做扎实——客服、代码助手、文档问答、语音输入。这些讨论有一个共同的前提:AI 应用只操作自己闭环内的数据。它读自己的知识库、检索自己的向量库、调用自己注册的工具,权限边界由应用自己的代码控制。

但 2026 年真正开始规模化的 AI 应用,正在系统性地越过这条边界。用户说"帮我把上个月逾期未付的三张发票都追一下",应用需要登录用户的邮箱、找出三张发票、访问账单系统、生成催款邮件、记下跟进状态。这条链路里的每一次调用,权限都不是应用自己的,而是用户委托出去的。一旦应用开始代用户触碰第三方系统,授权就从"应用的一个配置项"变成了"应用的一个运行时基础设施"——而这恰恰是当前公开讨论里最薄的一层。

本文要回答的问题是:当一个 AI 应用以 OAuth 客户端的身份,代表用户去访问第三方 API 时,凭证的边界应该怎么划?这不是"怎么在 prompt 里写得安全"的问题,而是四个互相咬合的协议问题:同意从哪来、凭证给谁用、动作覆盖到哪、用户怎么收回。

需要先声明本文的边界。第一,本文不讨论 Agent 内核机制,也不讨论模型选型与提示词技巧——那些是别的问题。第二,本文不讨论自建权限体系(企业内网里那种"部门-角色-资源"的 RBAC 表),那是另一套成熟学科。第三,本文的所有规范引用均为本文写作时(2026 年 10 月 27 日)的实测拉取结果,涉及 GitHub 仓库数据的一律标注拉取日期与星数快照;凡是没有公开数据支撑的判断,本文会显式标注为猜想。


一、问题的提出:代用户操作把授权变成了运行时问题

1.1 从"读自己的库"到"动别人的系统"

一次典型的代操作流程长这样:用户在自己的 AI 助手里说"把这封邮件里的发票报销流程走一下"。接下来发生的事情是:

  1. 应用需要读用户的邮箱——这需要一个代表该用户的 access token;
  2. 应用需要解析邮件正文、抽出发票信息——这通常靠模型,但抽取结果要落进业务库;
  3. 应用需要调财务系统创建报销单——这是另一个第三方系统,需要另一个凭证;
  4. 应用需要回复用户——这一步可能只是回消息,也可能是在第三方系统里真的回复。

这四步涉及至少两个独立的第三方系统、两个独立的授权服务器,以及一次模型输出直接驱动外部副作用的决策。传统 Web 应用里,这个形态的对应物是"第三方 OAuth 集成"——比如一个 SaaS 连接 Gmail。但在 AI 应用里,它多了一个传统 OAuth 客户端没有的东西:中间隔着一个不可信的决策器。

传统 OAuth 客户端的逻辑是确定的:用户在界面上点了"授权访问 Gmail 读取邮件",客户端拿到 token,此后所有调用都是客户端的既定代码路径在执行,授权范围与实际动作是一一对应的。而 AI 应用的逻辑是:用户说了一句话,模型在运行时决定调哪个 API、带什么参数。授权范围与实际动作之间隔着一层概率性推理——这层推理会不会越界,不取决于客户端代码,而取决于模型。

这带来一个直接后果:在传统 OAuth 模型里,"授权"是一次性的、静态的;在 AI 应用里,"授权"必须变成动态的。同一个 token,模型可能用它在 3 秒内只读一次邮件,也可能用它在一次会话里连续发起 20 次写操作,其中几次的语义边界由模型自己临时判断。你无法在授权那一刻就确定这次授权的实际行为。

1.2 三个被反复混淆的"授权"

讨论这个问题时,业界经常把三件不同的事混为一谈,而它们在工程上需要完全不同的解法:

第一件:应用访问自己拥有的资源。 比如应用读自己的数据库、调自己部署的向量库、访问自己的内部 API。这不是授权问题,是服务间身份问题——用 mTLS、API key 或工作负载身份解决,不涉及用户同意。

第二件:应用代表用户访问第三方。 也就是本文讨论的场景。用户必须显式同意,且同意范围必须被机器可执行地约束住。

第三件:应用内部把能力限制给模型。 也就是"能力令牌"模式:应用自己签发一张限时限域的令牌给模型,令牌里写明允许调用哪些工具、允许传什么参数、有效期多长、能不能重放。这解决的是"模型会不会乱调工具"的问题。

这三件事的正交性经常被误判。一个成熟的 AI 应用三层全都有:它有自己的服务间身份(第一件),它代表用户连着用户的邮箱和财务系统(第二件),它给模型发受限的能力令牌(第三件)。第二件和第三件尤其容易混:它们都在"限制模型能做什么",但约束的对象完全不同——能力令牌约束模型能调用哪些本地工具;委托授权约束应用能代表用户动哪些外部系统、动到什么程度。前者的信任边界在应用内部,后者的信任边界跨越了组织边界,攻击面、失效模式、审计要求、撤销机制全都不同。

理解了这条边界,本文才有讨论的落点。

1.3 为什么现有 AI 工程讨论几乎不谈这一层

一个直接的观察是:过去两年大量关于 AI 应用安全的文章,讨论的注入防御、护栏分层、工具白名单、审计日志,聚焦的都是"模型会不会被骗去调用不该调的工具"。这类问题有一个共同前提——工具的权限边界由应用自己定义,并且应用自己是执行者。模型被注入攻击骗了,最坏结果是应用内部某个工具被误调用,应用可以在代码里加校验、加审批、加沙箱。

代操作把信任边界推到了应用之外。模型被注入攻击骗了,最坏结果可能是应用拿着用户的合法凭证,在用户的第三方系统里执行了用户从未授权的动作。应用自己代码里的校验拦不住这个——因为从第三方系统的角度看,这个请求携带的是完全合法的用户凭证,请求格式完全合规,scope 完全匹配,audience 完全正确。所有能在应用内部做的校验都通过了,伤害发生在应用控制不了的地方。

这是本文要填的空。


二、委托授权的四层边界模型

在进入具体协议之前,先把问题形式化。代用户操作的授权问题可以拆成四个必须分别回答的问题,每个问题对应一层边界:

层边界问题失败时的具体形态主要协议机制
L1 主体边界谁同意了?同意的是谁?用户在 A 系统授权,令牌却被用于 B 系统的操作授权码流 + PKCE + 精确重定向匹配
L2 资源边界这份凭证能被送到哪里去?令牌被转发给下游 API,下游误以为令牌已由上游验证过RFC 8707 resource 参数 + audience 校验 + 禁止透传
L3 动作边界同意的范围覆盖哪些操作?授权了读,却执行了写;授权了 A 资源,却动了 B 资源scope + resource indicators + 逐动作策略
L4 时间边界用户怎么收回?用户在第三方系统点了"断开连接",应用的会话还在用旧令牌RFC 7009 撤销 + RFC 7662 内省 + 短期令牌

四层必须分开设计,因为它们的失效模式、检测手段和修复路径都不一样。把它们混成"权限"这一个概念,是很多实现出问题的根源。

2.1 L1 主体边界:同意的来源与绑定

L1 要回答的是"这份授权到底代表谁"。这一层的核心不是加密,而是确保授权请求与令牌兑换之间存在不可伪造的绑定。

OAuth 授权码流的基本形状是:客户端把用户浏览器重定向到授权服务器的授权端点,用户在授权服务器页面上做同意与选择,授权服务器返回一个授权码,客户端拿着授权码去令牌端点换 access token。这条流程里有一个关键的密码学结构:只有发起授权请求的那一方,才能把授权码兑换成令牌。

这个保证在 OAuth 2.0 里由 client_secret 提供——机密客户端在令牌端点出示密钥。但当客户端是公开客户端(桌面应用、移动应用、以及绝大多数 AI 应用里的自定义连接器)时,client_secret 根本不可信——它就写在应用里或配置里。RFC 9700(OAuth 2.0 Security Best Current Practice)在 §4.5.3.1 给出的对策是 PKCE(Proof Key for Code Exchange,RFC 7636):客户端在授权请求里带上自己生成的 code_challenge(SHA-256 后的哈希),在令牌请求里带上原始的 code_verifier,授权服务器校验两者匹配。

OAuth 2.1 草案(draft-ietf-oauth-v2-1-13,本文写作时为 -13 版,2025 年 5 月 28 日发布)把 PKCE 从"可选加固"提到了"默认必需"。规范第 7.5 节的原文措辞是:"授权服务器 MUST 支持 code_challenge 和 code_verifier 参数。客户端 MUST 使用 code_challenge 和 code_verifier,授权服务器 MUST 强制执行它们的使用。"规范第 10 节列出的与 OAuth 2.0 的差异中,第一条就是:"授权码流扩展了 PKCE 的功能,使得根据本规范使用授权码流的默认方法需要添加 PKCE 参数。"

值得强调的是 PKCE 在 AI 场景里的含义被低估了。AI 应用生态里大量存在"用户填一个 API Key"式的第三方集成——用户去第三方服务拿一把长期 key,粘到 AI 应用的配置页里。这种集成方式彻底绕过了授权码流,也就绕过了同意的语义。用户没有在第三方服务界面上做过任何授权决定,他只是把一把万能钥匙交给了 AI 应用。这把钥匙往往权限极大、生命周期极长、且没有任何撤销路径——除非用户自己去第三方服务后台手动删掉。

这不是"用户偷懒",而是 AI 应用的集成体验决定的:很多第三方服务根本不提供 OAuth,只提供 API Key。于是工程上的正确做法不是劝用户别这么做,而是在应用侧提供一个 OAuth 优先、API Key 兜底的接入层,并对走 API Key 兜底路径的集成打上明确的降级标记:这类连接不具备细粒度 scope、不具备可撤销性、其同意记录只能是"用户在我们的应用里贴了一把钥匙"这一条日志。

2.2 L2 资源边界:令牌的受众与 confused deputy

L2 是代操作场景里最容易被忽略、也最危险的一层。它回答的问题是:这份 access token 可以被送到哪里去用?

问题源于 OAuth 的一个结构性事实:access token 是 bearer token——谁拿着谁就能用,默认不绑定持有者。RFC 6750 §5.2 明确要求把令牌的预期接收方包含在令牌内,正是为了防止"令牌重定向"(token redirection):即一个资源服务器把令牌原样转发给另一个资源服务器。

在传统架构里这个问题不太容易发生,因为调用链是静态的:客户端 A 只跟资源服务器 B 通信,B 不会把 A 的令牌转发给 C。而在 AI 应用里,调用链是动态的:模型在运行时决定调哪个第三方 API。应用代码里写着"如果模型返回 tool_call(type='send_email') 就调邮箱 API",但模型可能返回任何已注册的工具名,包括一个内部会把令牌转发给第三方服务的工具。

MCP 的授权规范把这个问题称为 confused deputy(混淆代理)问题,并在 2025-06-18 版中给出了明确的规范性要求。规范原文指出:"攻击者可以利用充当第三方 API 中介的 MCP 服务器,导致 confused deputy 漏洞。通过使用窃取的授权码,他们可以在没有用户同意的情况下获取 access token。"

针对代理类 MCP 服务器,规范进一步要求:"使用静态 client ID 的 MCP proxy server MUST 在转发到第三方授权服务器之前,为每个动态注册的客户端获取用户同意(第三方授权服务器可能要求额外同意)。"这一条直指一个非常具体的工程陷阱:代理服务器用一个静态 client ID 去代表所有用户访问上游,用户的同意因此被折叠成了一次——上游看到的是同一个客户端在请求授权,而不是不同用户在各自授权。

RFC 8693(OAuth 2.0 Token Exchange)在第 1.1 节从另一个角度定义了同一件事的解法,这会在第五节展开。

2.3 L3 动作边界:同意的范围

L3 回答"同意的范围覆盖哪些操作"。这一层的传统机制是 scope——一个空格分隔的字符串,声明本次授权允许的操作类别。RFC 6749 定义了它的语义,但没有规定具体粒度;实践中不同 API 提供方的 scope 粒度差异极大,从 read / write 到 https://api.example.com/users/1234/messages 这样精确到单个资源路径的形式都有。

RFC 8707(Resource Indicators for OAuth 2.0)补上了另一半:scope 表达"能做什么操作",resource 表达"能在哪个资源上做"。规范第 1 节指出 OAuth scope "有时被超载用来传达受保护资源的位置或身份,但这并不总是可行或可取"。§2.1.1 定义了 resource 参数的值必须是绝对 URI,可以带 query 组件,但 MUST NOT 带 fragment 组件,并且允许传入多个 resource 参数来表示令牌用于多个目标资源。规范同时说明了 audience 与 resource 可以一起使用,表示"多个目标服务,混合了逻辑名与资源 URI"。

对于 AI 应用,L3 的额外难点是:scope 是声明式的,粒度由第三方服务定义,而模型的动作是程序性的、组合性的。用户在授权时同意的是 messages.read,模型在运行时做的却是"读第 3 封邮件里的附件,然后把它转发给另一个收件人"——这已经不是一次 messages.read 了,但每一次单独的 API 调用都落在 scope 之内。

这意味着scope 检查是必要但不充分的。它能拦住"读了没授权的邮件",拦不住"用已授权的读操作组合出一个未授权的效果"。后者要靠 L3 之上的应用层策略——对高风险动作组合设显式规则,对写操作要求额外确认,对"读-转发"这类二步组合做意图级校验。这块能力在多数 AI 框架里是缺失的,本文的落地清单里会单列。

2.4 L4 时间边界:同意的有效期与收回

L4 回答"这份同意什么时候失效,用户怎么收回"。这一层是代操作场景特有的痛点:传统 OAuth 客户端里,用户去第三方后台点"撤销授权",客户端下次用令牌时收到 401,流程自然闭环。但在 AI 应用里,用户往往是在 AI 应用里表达"别再连着我的邮箱了"——这句话需要被翻译成一次真正的令牌撤销,而不是一句友好的确认。

MCP 授权规范在 Token Theft 一节给出了直接的规范性要求:"客户端和服务端 MUST 实现安全的令牌存储并遵循 OAuth 最佳实践。授权服务器 SHOULD 签发短期的 access token 以降低泄露令牌的影响。对于公有客户端,授权服务器 MUST 按照 OAuth 2.1 §4.3.1 轮换 refresh token。"

"授权服务器 SHOULD 签发短期 access token"这一条对 AI 应用尤其关键,因为 AI 应用的令牌暴露面远大于传统客户端:令牌会进入日志(trace)、进入 trace 采样系统、进入会话持久化存储、可能进入模型的上下文(如果工具返回里带了它)、甚至进入错误上报系统。缩短令牌的存活时间,是把"令牌泄露后的可用窗口"从"以天计"压到"以分钟计"的最直接手段。


三、授权码流与 PKCE:把"同意"从模型手里拿回来

前三节是问题定位,这一节开始进入具体实现。第一个实现要点是:授权流程中涉及用户决策的每一个环节,都必须发生在模型看不到、也改不了的地方。

3.1 一条不可协商的规则

代操作应用里有一条规则应该被写进代码规范而非编码规范:模型的输出永远不能决定授权请求的参数。

具体来说,以下这些值只能来自应用代码,不能来自模型输出:客户端 ID、redirect_uri、scope 列表、resource 参数、audience、以及"是否需要用户重新同意"。模型可以决定"要发一封催款邮件",但不能决定"用哪个邮箱账号的令牌发""这笔钱是否需要财务主管的令牌"。

这不是洁癖。授权码流的安全性建立在"授权请求的参数由可信客户端生成"这个前提上:如果模型能改 scope,它就能在用户只同意读的时候把 write 加进去;如果模型能改 resource,它就能把令牌引到自己能读的地方。

3.2 MCP 授权规范给出的完整流程骨架

MCP 2025-06-18 版授权规范给出了目前最完整的一份"AI 应用代表用户访问第三方服务"的参考流程。规范第 4 节"授权流程步骤"用时序图明确列出了每一步,翻译成工程语言是这样的:

  1. 客户端发起无令牌请求。客户端向 MCP 服务器发请求但不带 access token。
  2. 服务器返回 401 并带 WWW-Authenticate 头。规范要求:"MCP 服务器 MUST 在返回 401 Unauthorized 时使用 WWW-Authenticate 头,以指示 resource server 元数据 URL 的位置。"注意这个 WWW-Authenticate 不只是"未授权"的意思,它同时承载了"去哪里找授权服务器"的发现信息——这是 RFC 9728 引入的机制。
  3. 客户端取受保护资源元数据。客户端按 RFC 9728 请求受保护资源元数据。RFC 9728 定义了 resource(必填)、authorization_servers(可选,JSON 数组)、bearer_methods_supported 等字段,并且规定:"如果受保护资源元数据是从受保护资源通过 WWW-Authenticate 的 resource_metadata 参数返回的 URL 获取的,那么返回的 resource 值 MUST 与客户端用于向资源服务器发起请求的 URL 完全相同。如果这些值不相同,响应中包含的数据 MUST NOT 被使用。"这是一个防止元数据被替换的硬约束。
  4. 客户端解析并选定授权服务器。规范明确:"Protected Resource Metadata 文档可以定义多个授权服务器。选择使用哪个授权服务器的责任在于 MCP 客户端,遵循 RFC 9728 §7.6 的指南。"
  5. 客户端取授权服务器元数据。客户端 GET /.well-known/oauth-authorization-server。RFC 8414 规定了这个 well-known URI 的插入规则——"在授权服务器的 issuer 标识符中,把 well-known URI 字符串插入到主机组件与路径组件之间",默认路径是 /.well-known/oauth-authorization-server,且 MUST 使用 https 方案。
  6. (可选)动态客户端注册。规范表述为:"MCP 客户端和授权服务器 SHOULD 支持 OAuth 2.0 动态客户端注册协议 RFC 7591,以允许 MCP 客户端在无需用户交互的情况下获得 OAuth 客户端 ID。这对 MCP 至关重要,因为客户端不可能预先知道所有可能的 MCP 服务器及其授权服务器。"
  7. 生成 PKCE 参数并带上 resource 参数,打开浏览器进入授权 URL。
  8. 用户在授权服务器页面上做决定。授权服务器展示它自己的同意界面。
  9. 带授权码重定向回客户端回调地址。
  10. 客户端拿 code_verifier + resource 去令牌端点换令牌。
  11. 客户端带 access token 发起 MCP 请求。

第 6 步的动态客户端注册值得单独说一句。它解决的是 AI 应用的一个真实痛点:如果每个第三方服务都需要用户手工填一个 client ID,这个集成就没法做产品化。动态注册让客户端在需要时自动申请一个身份。但动态注册同时也放大了 §2.2 提到的那个陷阱——代理服务器用一个静态 client ID 代表所有用户。所以规范同时给出了那条"每个动态注册的客户端必须单独获得用户同意"的要求。

3.3 授权请求里必须带的东西

综合 OAuth 2.1 与 MCP 规范,一个代操作应用的授权请求至少要包含这些:

  • PKCE 的 code_challenge 与 code_challenge_method(S256)。OAuth 2.1 要求客户端 MUST 使用、服务器 MUST 强制执行。RFC 7636 §4.2 给出了 code_challenge 的 ABNF:code-challenge = 43*128unreserved,字符集是 ALPHA / DIGIT / - / . / _ / ~。
  • resource 参数。MCP 规范:"MCP 客户端 MUST 在授权和令牌请求中包含 resource 参数"。RFC 8707 §2.1.1 规定它必须是绝对 URI,且不带 fragment。
  • 精确匹配的 redirect_uri。OAuth 2.1 第 10 节明确:"重定向 URI 必须按 RFC 9700 §4.1.3 使用精确字符串匹配进行比较"。RFC 9700 §2.1(其中 §2.1.1 针对授权码流)的措辞更严格:"在把客户端重定向 URI 与预注册 URI 比较时,授权服务器 MUST 使用精确字符串匹配,原生应用的 localhost 重定向 URI 的端口号除外。"精确匹配这一条防的是重定向 URI 注入与开放重定向。
  • state 参数(若用)。OAuth 2.1 §7.5 附近的规范还提醒:"state 和 scope 参数 SHOULD NOT 包含明文的敏感客户端或资源所有者信息,因为它们可能在不安全的信道上传输或在不安全的存储中保存。"

OAuth 2.1 相对 2.0 还有一处容易被漏掉的简化:第 10.2 节说明为什么令牌请求里的 redirect_uri 参数被移除了——"在 OAuth 2.1 中,授权码注入攻击由 code_challenge 和 code_verifier 参数阻止,使得在令牌请求中包含 redirect_uri 参数不再有意义。因此它被移除了。"这解释了 OAuth 2.1 第 10 节列出的另一条变化:"包含授权码的令牌端点请求不再包含 redirect_uri 参数。"如果你的实现里还在令牌请求里带 redirect_uri,说明实现的是 OAuth 2.0 语义,需要检查是否与服务器的实现一致。


四、受众绑定与 confused deputy:为什么不能把令牌转发下去

第四节处理 L2 资源边界,这是 MCP 授权规范里规范性要求最密集的部分,也是最容易出事的部分。

4.1 两条独立的失效路径

MCP 规范"访问令牌权限限制"(Access Token Privilege Restriction)一节把这个问题拆成"两个关键维度",原文措辞相当直接:

维度一,受众校验失败。 "当 MCP 服务器没有验证令牌是专门为它签发的(例如通过 audience claim,见 RFC 9068)时,它可能接受最初为其他服务签发的令牌。这打破了 OAuth 的基本安全边界,允许攻击者在非预期服务之间复用合法令牌。"

维度二,令牌透传。 "如果 MCP 服务器不仅接受 audience 错误的令牌,还把这些未经修改的令牌转发给下游服务,它可能导致'混淆代理'问题,即下游 API 可能错误地信任该令牌以为它来自 MCP 服务器,或假设该令牌已被上游 API 验证过。"

规范随后给出的强制性要求是:"MCP 服务器 MUST 在处理请求前验证访问令牌,确保访问令牌是专门为该 MCP 服务器签发的,并采取一切必要措施确保不会向未授权方返回数据。MCP 服务器 MUST 遵循 OAuth 2.1 §5.2 的指南来验证入站令牌。MCP 服务器 MUST 只接受专门为自身签发的令牌,MUST 拒绝那些未包含自身于 audience claim 或以其他方式未验证自己就是预期接收方的令牌。"

4.2 "只接受自己的令牌"这句话的工程含义

上面这段规范看起来平淡,但它在工程上意味着一条相当严格的不变式:令牌绝不跨越服务边界原样传递。

具体到 AI 应用,一个反直觉但必须接受的结论是:如果你的 AI 应用(作为 MCP 服务器)要访问上游第三方 API,它不能把用户给它的令牌原样转发过去。MCP 规范在同一段落里给出了正解:"如果 MCP 服务器要向上游 API 发起请求,它可以作为它们的 OAuth 客户端 acting。上游 API 处使用的 access token 是一个独立的令牌,由上游授权服务器签发。"

也就是说,正确形态是令牌交换(token exchange),而不是令牌转发:用户给应用的是令牌 A(受众是应用自己),应用拿着令牌 A 去上游授权服务器换一个令牌 B(受众是上游 API),然后用令牌 B 调上游。

令牌透传为什么危险,可以用一个小例子说明。假设你的 AI 应用接了一个"邮件读取"工具和一个"发送邮件"工具,你的应用自己也是一个邮件服务的客户端。用户授权时给的是受众为"你的应用"的令牌。如果你的应用把同一个令牌原样转发给邮件服务的下游 API(比如附件存储服务),而下游 API 只检查"这个令牌有效"而不检查 audience,那么:用户给"读邮件"的授权,实际上变成了对附件存储服务的读写授权——而用户从没有对后者表示过任何同意。

RFC 8693 §5 的安全考量部分还补了一个更根本的问题:传递令牌会破坏责任归属。如果令牌在多个服务间流动,一旦发生泄露,你无法确定泄露发生在哪一环,也就无法精确撤销。

4.3 audience 校验的具体做法

规范要求按 RFC 8707 §2 验证令牌是专为本服务器签发的。RFC 8707 §1 对这个机制的原理有很好的表述:"重要的安全假设之一是,访问令牌必须只对特定受保护资源和特定范围有效。例如 RFC 6750 §5.2 规定在令牌中包含其预期接收方,以防止令牌重定向。当授权服务器被告知处理该令牌的资源时,它可以把该令牌的预期受众限制到给定资源,使得该令牌无法在其他资源上成功使用。"

工程上,audience 校验至少有三种实现路径:

路径一,JWT 令牌走 claim 校验。 令牌是 JWT 时,检查 aud claim 包含本服务标识符。这是最直接的实现,MCP 规范点名了 RFC 9068(JSON Web Token Profile for OAuth 2.0 Access Tokens)。

路径二,不透明令牌走内省。 令牌是不透明字符串时,按 RFC 7662 调用授权服务器的内省端点,返回的 aud 字段中必须包含本服务标识符。RFC 9728 的元数据里也有 bearer_methods_supported 字段(示例值 ["header", "body"]),但 MCP 规范在 Token Requirements 一节明确要求客户端走头部:"MCP 客户端 MUST 使用 OAuth 2.1 §5.1.1 定义的 Authorization 请求头字段",示例是 Authorization: Bearer eyJhbG...s...,并且**"访问令牌 MUST NOT 被包含在 URI 的 query string 中"**。

路径三,两种都要做。 实际生产系统里令牌格式往往混杂(自签发的 JWT 与第三方签发的不透明令牌并存),所以校验逻辑要按令牌的 typ 或签名格式分流。

无论哪条路径,有一条来自 OAuth 2.1 第 10 节的规范性约束必须遵守:"Bearer token 的使用省略了在 URI query string 中使用 bearer token"——即绝不能把令牌放在 URL 里。原因是 URL 会被写进日志、浏览器历史、Referer 头和代理服务器日志。

4.4 错误码语义也属于协议

MCP 规范给了一张三行的错误码表,工程含义值得展开:

状态码含义场景
401Unauthorized需要授权或令牌无效
403Forbiddenscope 无效或权限不足
400Bad Request授权请求格式错误

这张表背后的规范措辞是:"无效或过期的令牌 MUST 收到 HTTP 401 响应。"

区分 401 与 403 在 AI 应用里不是细节洁癖,而是决定了恢复策略:401 意味着"重新授权"(通常要引导用户去第三方完成同意),403 意味着"你不该做这件事"(应该拒绝而不是重试)。很多实现在这里偷懒,把所有鉴权失败都当成 401,结果模型遇到 403 时反复触发重新授权流程——用户在应用里被反复弹授权窗口,而真正的越权尝试被当成"令牌过期"静默重试。


五、委托 vs 冒充:RFC 8693 的两套语义与它的边界

第五节处理一个更本质的问题:应用拿用户令牌去替用户操作时,这件事在协议上到底叫什么?答案不是唯一的,而不同的答案会导致完全不同的实现。

5.1 两种语义

RFC 8693 §1.1 用一个非常具体的场景定义了两种模式:"一个常见的用例是让资源服务器 A 代表请求用户 B 调用后端服务 C。根据本地站点策略和授权基础设施,可能希望 A 使用自己的凭证访问 C,并附上某种形式的标注,表明 A 正在代表 B('委托');或者希望 A 被授予对 C 的受限访问凭证,但该凭证仍继续标识 B 为授权实体('冒充')。"

翻译成 AI 应用的语境:

委托(delegation)。 审计日志里留下两个主体:用户 B 和应用 A。令牌由上游授权服务器签发,act claim 里记录 actor 是 A。上游系统知道"这是 A 在执行,但代表的是 B"。责任可追溯,能力可归属。

冒充(impersonation)。 上游系统只看到 B。令牌在语义上等同于 B 亲自发起的请求,应用 A 完全隐身。实现简单,上游无需改造,但审计链条断了一环——事后无法回答"这条操作是用户本人点的,还是 AI 做的"。

这个区别在很多场景下不是学术洁癖,而是决定用户能否撤销以及能否追责的关键。用户在第三方系统的"活动日志"里如果只看到自己的操作,他无法判断是不是 AI 干的,也就无法在出问题时准确定位责任方。

5.2 协议怎么表达

RFC 8693 定义了表达这两种语义的机制。请求参数方面:subject_token 与 subject_token_type 是必填的——"代表请求发起方身份的安全令牌,通常其 subject 就是响应请求所签发的安全令牌的 subject";actor_token 与 actor_token_type 是可选的——"代表行为方身份的安全令牌,通常是被授权使用所请求安全令牌并代表 subject 行事的一方"。规范特别规定:"当请求中出现 actor_token 参数时 actor_token_type 是 REQUIRED 的,但在其他情况下 MUST NOT 出现。"

响应与声明方面,规范定义了 act(Actor)claim、scope claim、client_id claim,以及 may_act(Authorized Actor)claim。其中 may_act 的定义最贴近 AI 应用的场景:"may_act claim 陈述某一方被授权成为 actor 并代表另一方行事。该 claim 可以这样使用,例如,当 subject_token 在令牌交换请求中被提交到令牌端点时,subject token 中的 may_act claim 可以被授权服务器用来判断客户端(或 actor_token 所标识的一方)是否被授权进行所请求的委托或冒充。"

这给了 AI 应用一个具体可用的机制:用户可以在令牌里预置"我授权这个应用代我操作"这个断言,授权服务器在交换时校验这个断言是否覆盖了当前请求的 actor 与资源范围。相当于把 L1/L3 的一部分判断从应用侧前移到了授权服务器侧——授权服务器是可信的,它说的话比模型说的话可信。

5.3 token exchange 的现实约束

必须诚实说明:RFC 8693 虽然是 2019 年的标准,但在 AI 应用生态里的实际落地率并不高。这不是它设计得不好,而是有几个现实原因。

第一,授权服务器的支持度不均。RFC 8693 需要授权服务器实现令牌交换端点,而 OAuth 生态里这个端点远不如授权码流那样普及。规范的 §1 也承认:"本规范的适用范围仅限于定义一个使用 OAuth 2.0 的 STS 风格令牌交换的基本请求-响应协议。虽然定义了少量新的 JWT claim 来表达委托语义,但这些令牌本身(无论是客户端提交的令牌还是客户端获得的令牌)的具体语法、语义与安全特性明确不在本规范范围内,也没有要求……"换句话说,RFC 8693 定义的是协议形状,具体的令牌语义和安全特性留给实现——这直接导致互操作性差。

第二,委托链的信任建立是难题。当委托链超过两层(A 代表 B 访问 C,C 又代表 A 访问 D),每一跳的信任关系都需要独立建立和验证,而规范对此只有原则性描述。

第三,MCP 规范本身也没有把 token exchange 作为强制要求。它要求的是 audience 校验与禁止透传——这两条在只有一层委托(应用 → 第三方)时是足够的,因为应用的令牌受众就是应用自己,应用再自己拿一个上游令牌即可。

因此本文的立场是:在只有一层委托的常见场景下,优先实现"应用自己换取上游令牌",而不是追求完整的 RFC 8693 委托链。当确实需要多跳委托(比如应用 A 代表用户操作应用 B,B 再代表用户操作 C)时,才需要引入 RFC 8693 的 act / may_act 机制——而且即便如此,也应该只在第一跳使用标准的令牌交换参数,后续跳用应用自定义的传递方式并在两端显式记录信任关系。


六、凭证的自我保护:DPoP、短期令牌与刷新令牌轮换

第六节是 L4 时间边界的技术实现。核心命题很简单:令牌的暴露面越大,存活时间就必须越短。

6.1 AI 应用的令牌暴露面

先把问题摆清楚。AI 应用里的一个 access token,可能出现在这些地方:

  • 应用日志与 trace(尤其是把请求头全量记录的那种日志);
  • 会话持久化存储(为了多轮对话恢复上下文);
  • 工具返回值(如果某个工具把上游响应头带回来了);
  • 错误上报系统(SDK 自动捕获请求上下文);
  • 模型的上下文窗口(如果工具描述或工具结果里出现了令牌);
  • 内存中的多个副本(重试、并发、连接池)。

RFC 9700 §4.10 有一节专门讲"被盗访问令牌的滥用",§4.9.2 讲"被攻陷的资源服务器",§4.10.1 讲"发送方约束的访问令牌",§4.10.2 讲"受众受限的访问令牌"。这些章节的存在本身就说明:这是 OAuth 生态里公认的高危面,而 AI 应用的暴露面比传统客户端大一个量级。

6.2 DPoP:把 bearer 变成 proof-of-possession

RFC 9449(OAuth 2.0 Demonstrating Proof of Possession at the Application Layer,2024 年 8 月)给出了发送方约束(sender-constrained)的一个标准化实现。核心机制是:客户端为每次请求生成一个 DPoP proof——一个用客户端私钥签名的 JWT——并把它与访问令牌一起发送。资源服务器不只验证令牌,还验证 proof 的签名密钥与令牌绑定的密钥一致。

规范 §4.2 规定了 proof 的结构。JOSE 头部 MUST 至少包含:typ(值为 dpop+jwt)、alg(签名算法)、jwk(公钥)。payload MUST 至少包含以下 claim:

  • jti:proof JWT 的唯一标识符。规范要求"该值 MUST 被赋予,使得在有效期内同一上下文中的任何其他 DPoP proof 获得相同值的概率可忽略",并说明"jti 可被服务器用于重放检测与防范(见 §11.1)"。
  • htm:请求的 HTTP 方法。
  • htu:请求的 HTTP target URI,去掉 query 与 fragment 部分。
  • iat:JWT 的创建时间戳。

当请求中携带访问令牌时,proof MUST 额外包含:

  • ath:"访问令牌的哈希。该值 MUST 是对该访问令牌值的 ASCII 编码做 SHA-256 哈希后再做 base64url 编码的结果。"

当服务器发起 nonce 挑战时,proof MUST 额外包含:

  • nonce:"通过 DPoP-Nonce HTTP 头提供的近期 nonce。"

htm + htu + ath 三者合起来构成了"这个 proof 只能用于这个方法、这个 URL、这个令牌"的绑定——攻击者即使窃取了令牌,也无法把它挪到别的接口上使用。而 jti 提供了重放检测的基础。

规范还解释了 nonce 的必要性:"为此,服务器可以(可选地)要求客户端在 proof 中包含一个由服务器选择的、攻击者无法预测的值(nonce)。在没有这个可选 nonce 的情况下,预计算的 DPoP proof 的影响在某种程度上是受限的,因为 proof 是与受保护资源访问中的访问令牌绑定的。由于覆盖尚不存在的访问令牌的 proof 无法被实际计算出来……"

对 AI 应用而言,DPoP 的实际部署形态有一个现实约束:客户端私钥必须存在客户端本地。桌面/移动端可以用系统钥匙串或安全 enclave;服务端场景(AI 应用后端代用户调第三方)则可以用 KMS 或 HSM 管理密钥,或者退一步用软件密钥 + 短轮换。这是 sender-constrained 的另一条实现路径——RFC 8705(OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens)规定了两类方法:"使用自签证书或公钥基础设施(PKI)",并定义了"自签证书互 TLS 方法"与"PKI 互 TLS 方法",以及第三种"互 TLS 客户端证书绑定访问令牌"。

6.3 刷新令牌轮换:截断重放窗口

MCP 规范引用了 OAuth 2.1 §4.3.1 的刷新令牌轮换要求,RFC 9700 §2.2.2 的措辞是:"公有客户端的刷新令牌 MUST 是发送方约束的,或使用 §4.14 所述的刷新令牌轮换。"

RFC 9700 §4.14 描述的轮换机制(§2.2.2 的强制性要求引用了它)是:每次用刷新令牌换新访问令牌时,授权服务器同时签发一个新的刷新令牌,旧的立即失效。如果检测到旧刷新令牌被再次使用(意味着令牌可能已泄露),授权服务器应当撤销整个授权链——这个模式通常叫重放检测。

OAuth 2.1 第 10 节的差异清单里也重复了这一条:"公有客户端的刷新令牌必须是发送方约束的或一次性的,按 RFC 9700 §4.13.2。"同时另一条值得注意:"授权服务器必须在请求体中支持客户端凭据(client credentials)"——这让服务端场景可以用标准机器身份,而不是自己发明一套。

RFC 7009(OAuth 2.0 Token Revocation)规定了撤销端点的行为:客户端通过它通知授权服务器某个刷新令牌或访问令牌不再需要。规范的第 2 节给出了一条关键要求:"实现 MUST 支持刷新令牌的撤销,SHOULD 支持访问令牌的撤销。"以及撤销的时序语义:"授权服务器随后使令牌失效。失效立即发生,撤销后该令牌不能再被使用。在实践中可能存在传播延迟,例如某些服务器知道撤销而另一些不知道。"

这个"传播延迟"在 AI 应用里是可测量的工程量,必须被显式管理——见 §8 的清单。


七、撤销与断权:让"用户收回授权"成为协议动作

第七节单独讨论撤销,因为它是代操作应用与普通 OAuth 集成之间体验差距最大的地方,也是最容易被"假装实现了"的地方。

7.1 三种撤销语义必须分清

在 AI 应用里,"撤销"这个词至少对应三种不同的技术动作,混淆它们会导致撤销功能形同虚设:

第一种,授权关系撤销。 用户不再允许应用代表自己访问第三方。技术上对应 RFC 7009 的撤销端点调用,撤销刷新令牌(以及可选的访问令牌),以及在授权服务器侧解除该授权关系。这是"永久性"的撤销。

第二种,令牌失效。 某个具体的 access token 提前失效(还没到自然过期时间)。技术上对应授权服务器侧的强制失效或黑名单。这是"临时性"的撤销。

第三种,会话中断。 用户在 AI 应用里说"先别动我的邮箱了"。这可能对应上面任何一种,也可能只是应用层的软开关——应用决定不再发起新的调用,但已经发出去的请求不管。

工程上必须区分清楚的原因是它们的生效时延不同:授权关系撤销要等授权服务器传播(RFC 7009 明确承认"可能存在传播延迟");令牌失效在自签发体系里可以做到毫秒级;应用层软开关是即时的但覆盖不全。

7.2 把用户的自然语言映射到协议动作

AI 应用的一个独特之处是,用户会用自然语言表达撤销意图。这既是机会也是陷阱——机会在于可以直接兑现用户的期望,陷阱在于模型可能把"暂停一下"误解成"彻底断开",或者反过来。

一个务实的映射规则是按后果的可逆性分级:

用户表达建议的协议动作可逆性
"先别动我的邮箱了" / "这次先别发"应用层会话开关 + 停止发起新调用随时恢复
"别再发邮件了,但读取还要"收窄 scope,重新走一次授权以获取收窄后的令牌需重新同意
"断开我的邮箱" / "取消授权"RFC 7009 撤销 + 停止使用 + 清理本地存储的令牌与相关会话状态需重新授权才能恢复
"我改了邮箱密码"撤销所有相关授权 + 标记连接为需重新授权(密码变更通常导致上游令牌批量失效)需重新授权

第三行里"清理本地存储的令牌与相关会话状态"经常被漏掉。撤销了上游令牌但本地还留着旧的 access token(虽然已无效)以及包含该令牌的会话记录,仍然是泄露面。

7.3 撤销必须可验证,而不是只能相信

一个常见的设计失误是:撤销动作是"发出去了",但没有验证它是否真的生效。RFC 7009 的撤销响应本身是 200 即成功——但那只意味着"授权服务器收到了并处理了撤销请求",不意味着所有资源服务器都已经知道了。

要真正验证撤销生效,可行的做法有两条:

用短令牌 + 主动失效。 把 access token 的存活时间压到几分钟(这也是 §6 的直接收益),撤销之后最多几分钟内旧令牌自然失效。这样"传播延迟"这个变量被时间轴吸收掉了。

用内省做最终裁决。 对需要严格保证的动作(比如转账、发外部邮件),在执行前用 RFC 7662 内省令牌状态,而不是信任本地缓存的令牌元数据。代价是每次都多一次网络往返,所以通常只对高风险动作做。


八、从规范到代码:自建连接器的十二项检查

前七节是原理,这一节是清单。以下十二项按"接入一个第三方服务"的粒度组织,每一项都标出了对应的规范依据,可直接作为代码评审与 CI 门禁的条目。

第 1 项:授权请求的参数来源审计。 逐条列出授权请求中的所有参数,标注每一项是来自应用代码还是来自模型输出。任何一项来自模型输出都是阻断级缺陷。依据:OAuth 2.1 §7.5、§10.2,以及 §3.1 的不变式。

第 2 项:PKCE 强制。 授权请求必须带 code_challenge(S256),令牌请求必须带 code_verifier,且服务器开启强制校验。依据:OAuth 2.1 §7.5 的 "Clients MUST use code_challenge and code_verifier and authorization servers MUST enforce their use"。

第 3 项:重定向 URI 精确匹配。 客户端只使用预注册的精确字符串 URI,不用任何通配或前缀匹配;服务端侧若无法做到精确匹配,这是需要换授权服务器的硬理由。依据:RFC 9700 §2.1、OAuth 2.1 §10。

第 4 项:resource 参数全链路携带。 授权请求与令牌请求都带 resource,且其值与客户端实际请求的资源服务器 URL 一致。依据:RFC 8707 §2.1.1、MCP 规范 Resource Parameter Implementation 一节。

第 5 项:audience 校验在每个资源服务器上独立实现。 不允许"网关校验过了下游就不用校验"的假设——每一跳都要独立校验 aud 是否包含自己。依据:MCP 规范 Access Token Privilege Restriction 一节。

第 6 项:禁止令牌透传。 代码里不应存在"把入站 Authorization 头原样转发到出站请求"的路径。需要在 CI 里加一条静态检查:搜索把请求头对象整体透传的代码模式。依据:MCP 规范同上。

第 7 项:令牌不落 URL、不进日志。 禁止把 access token 放进 query string;日志与 trace 里对 Authorization 头做脱敏;错误上报系统过滤请求头。依据:OAuth 2.1 第 10 节"MCP 客户端 MUST 使用 Authorization 请求头字段"及"MUST NOT 被包含在 URI 的 query string 中"。

第 8 项:短期令牌 + 刷新令牌轮换 + 重放检测。 access token 存活时间以分钟计;刷新令牌每次轮换;检测到旧刷新令牌复用时撤销整条授权链。依据:MCP 规范 Token Theft 一节、RFC 9700 §2.2.2 与 §4.14。

第 9 项:sender-constrained 能力评估。 至少评估能否启用 DPoP 或 mTLS 绑定;若不能,明确记录这个降级并相应缩短令牌存活时间。依据:RFC 9449、RFC 8705。

第 10 项:错误码分流。 401 触发重新授权流程,403 直接拒绝且不重试,400 视为客户端 bug。依据:MCP 规范 Error Handling 表。

第 11 项:撤销闭环。 应用内存在"断开连接"入口,调用 RFC 7009 撤销端点,清理本地令牌与会话;撤销状态有可验证的确认(内省或短令牌自然过期)。依据:RFC 7009 §2、RFC 7662。

第 12 项:代理与透传场景的额外同意。 若应用充当中介(用一个静态 client ID 代表多个用户访问上游),必须为每个用户获取独立同意,禁止折叠。依据:MCP 规范 Confused Deputy Problem 一节。

此外还有三项本文认为值得单独列出的工程实践,它们不在规范里,但直接决定这类应用能否长期存活:

组合动作的意图级校验。 scope 只能拦住单次越权调用,拦不住"读 A + 写 B"的组合。凡是把读到的内容转发到另一个系统的动作(外发邮件、提交工单、创建支付),都应设为需要显式确认的高风险动作。依据:§2.3 的分析。

授权状态与模型上下文的隔离。 令牌、令牌元数据、令牌错误信息都不应进入模型上下文。授权状态属于控制平面,模型属于数据平面,两者的信息流应当是单向的(模型只能通过"需要授权"这样的信号请求授权,不能读取授权细节)。依据:§3.1。

连接状态的显式建模。 授权状态至少有七种——未连接、已连接待确认、已连接可用、已连接但令牌过期、已连接但 scope 不足、已撤销、需重新授权。它们必须是有类型的枚举而不是布尔值,因为每种状态对应的用户提示与恢复动作完全不同。依据:§7.1。


九、局限、争议与未公开验证的猜想

本文的结论建立在公开规范与实测数据上,但有三类判断需要明确标注其证据强度。

第一类是实测确认的事实。 本文引用的所有 RFC 条款(RFC 9700、9449、8693、8707、9728、8414、7009、7662、9126、8705、7636)与 MCP 2025-06-18 授权规范原文,都是本文写作时拉取的规范正文,其中引号内的英文原文为直接摘录。GitHub 仓库数据为 2026-10-27 通过 GitHub API 实测的快照。

第二类是工程判断,有较强依据但非规范强制。 例如"优先实现应用自己换取上游令牌,而非追求完整 RFC 8693 委托链"——这依据的是规范中关于令牌语义超出范围的自我声明,加上 MCP 规范未把 token exchange 列为强制要求这两点。但不同授权服务器生态的实际支持度本文没有做完整普查,这是一个明确的缺口。

第三类是未公开验证的猜想。 以下三点本文倾向于如此判断,但没有找到公开数据或规范依据支撑,读者应视为待验证的假设:

其一,AI 应用生态中"API Key 兜底集成"的比例可能远高于 OAuth 集成。依据是大量第三方服务(尤其非消费级 SaaS)不提供 OAuth,以及本文 §2.1 观察到的 AI 应用集成本质上鼓励"贴一把钥匙"的低摩擦体验。但截至 2026-10-27 未见公开统计。

其二,confused deputy 类问题在真实 AI 应用中的实际发生率可能远低于理论暴露面。理由是多数代理场景下的令牌流是应用自己编排的,透传需要特定的代码形态;但这也可能只是因为这类事件通常不会公开披露。没有公开事故数据。

其三,缩短 access token 存活时间带来的重授权摩擦,可能成为 DPoP 等更强机制落地的实际阻力。这与 §7.3 的建议(用短令牌吸收传播延迟)形成一个真实的张力——本文倾向于认为对 AI 应用而言短令牌比复杂绑定更划算,但这个权衡依赖具体产品的使用频率,没有公开数据。

还有一个方法论上的自省。本文在选题阶段对全站 718 篇已发布文章做了三轮全量 body 词频扫描(第一轮 78 词、第二轮 150+ 词、第三轮 90+ 词,每轮覆盖全部文章的正文而非仅标题)。结果显示"委托授权"象限的一批关键词在全站正文中的命中数接近零,其中 RFC 8693、DPoP、confused deputy、token exchange、PKCE 这几个词组的命中数均为个位数到零。必须承认这个方法有明确的局限:它基于关键词匹配,一个用完全不同的词汇描述同一问题的文章会被漏掉;而作者也可能用隐喻的方式绕开术语。这与"词频 0 命中"本身的性质一致——它是必要条件不是充分条件。因此本文的实际依据是:词频扫描用于排除明显饱和的象限,最终的差异化论证建立在对近邻文章正文的逐段阅读上。


十、小结

回到开头那个例子:"帮我把这封发票的报销流程走一下。"

这条链路跨越两个第三方系统、两份用户凭证、一次模型决策,以及至少四个授权边界。把它做对,需要的不是更聪明的模型,而是把授权从"应用的一个配置项"改造成"应用的一个运行时基础设施"。

本文的四层边界模型——主体(L1)、资源(L2)、动作(L3)、时间(L4)——分别对应四组可执行的协议机制:授权码流加 PKCE 与精确重定向匹配解决 L1;RFC 8707 的 resource 参数与 audience 校验加禁止透传解决 L2;scope 加资源指示器加应用层组合动作校验解决 L3;短期令牌加刷新令牌轮换加 RFC 7009 撤销闭环解决 L4。

如果只能记住三句话:第一,令牌绝不能跨越服务边界原样传递,需要透传的地方用令牌交换。第二,令牌存活时间要按"它会出现在你的日志、trace 和会话存储里"这个事实来定,而不是按传统客户端的假设来定。第三,模型永远不能决定授权请求里的任何参数——同意是用户的决定,不是模型的输出。

代用户操作的授权工程在 2026 年仍处在标准先行、实践滞后的阶段。MCP 授权规范在 2025-06-18 版里已经把大部分边界写成了 MUST,而本文写作时(2026-10-27)多数 AI 应用框架尚未把这些 MUST 落到默认实现里。这个缺口本身就是一个明确的工程机会:谁先把授权边界做对,谁就能在代操作这个象限上先拿到生产级的信任。


参考文献

  1. RFC 9700, OAuth 2.0 Security Best Current Practice, 作者 T. van Oort, R. van Heuvel, J. Bradley 等, 2025 年 1 月. https://www.rfc-editor.org/rfc/rfc9700.txt
  2. RFC 9449, OAuth 2.0 Demonstrating Proof of Possession at the Application Layer (DPoP), 作者 B. Ho, M. Jones, N. Sakimura, 2024 年 8 月. https://www.rfc-editor.org/rfc/rfc9449.txt
  3. RFC 8693, OAuth 2.0 Token Exchange, 作者 M. Jones, A. Mannila, M. Bradley, 2019 年 9 月. https://www.rfc-editor.org/rfc/rfc8693.txt
  4. RFC 8707, Resource Indicators for OAuth 2.0, 作者 M. Jones, R. Bradley, N. Sakimura, 2020 年 2 月. https://www.rfc-editor.org/rfc/rfc8707.txt
  5. RFC 9728, OAuth 2.0 Protected Resource Metadata, 作者 M. Jones, N. Sakimura, 2025 年 3 月. https://www.rfc-editor.org/rfc/rfc9728.txt
  6. RFC 8414, OAuth 2.0 Authorization Server Metadata, 作者 M. Jones, N. Sakimura, 2018 年 6 月. https://www.rfc-editor.org/rfc/rfc8414.txt
  7. RFC 7009, OAuth 2.0 Token Revocation, 作者 T. Bradley, 2013 年 2 月. https://www.rfc-editor.org/rfc/rfc7009.txt
  8. RFC 7662, OAuth 2.0 Token Introspection, 编辑 J. Richer, 2015 年 10 月. https://www.rfc-editor.org/rfc/rfc7662.txt
  9. RFC 7636, Proof Key for Code Exchange by OAuth Public Clients, 作者 J. Richer 等, 2015 年 9 月. https://www.rfc-editor.org/rfc/rfc7636.txt
  10. RFC 8705, OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens, 作者 B. Campbell, J. Bradley, Yubico, N. Sakimura, T. Lodderstedt, 2020 年 2 月. https://www.rfc-editor.org/rfc/rfc8705.txt
  11. RFC 9126, OAuth 2.0 Pushed Authorization Requests (PAR), 作者 T. Lodderstedt, B. Campbell, N. Sakimura, D. Tonge, F. Skokan, 2021 年 9 月. https://www.rfc-editor.org/rfc/rfc9126.txt
  12. RFC 9068, JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens, 作者 R. Bradley 等, 2021 年 8 月. https://www.rfc-editor.org/rfc/rfc9068.txt
  13. RFC 6750, The OAuth 2.0 Authorization Framework: Bearer Token Usage, 作者 M. Erdelyi 等, 2012 年 10 月. https://www.rfc-editor.org/rfc/rfc6750.txt
  14. RFC 6749, The OAuth 2.0 Authorization Framework, 作者 D. Hardt, 2012 年 10 月. https://www.rfc-editor.org/rfc/rfc6749.txt
  15. draft-ietf-oauth-v2-1-13, The OAuth 2.1 Authorization Framework, 作者 D. Hardt, A. Parecki, T. Lodderstedt, 2025 年 5 月 28 日发布. https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.txt
  16. Model Context Protocol 授权规范(2025-06-18 版)Authorization 章节,modelcontextprotocol/modelcontextprotocol 仓库 main 分支实测拉取于 2026-10-27. https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/docs/specification/2025-06-18/basic/authorization.mdx
  17. modelcontextprotocol/servers GitHub 仓库(91,597 星,11,869 forks,TypeScript,license 字段为 NOASSERTION),GitHub API 实测于 2026-10-27. https://github.com/modelcontextprotocol/servers
  18. modelcontextprotocol/typescript-sdk GitHub 仓库(13,661 星,2,346 forks,TypeScript),GitHub API 实测于 2026-10-27. https://github.com/modelcontextprotocol/typescript-sdk
  19. modelcontextprotocol/modelcontextprotocol GitHub 仓库(9,541 星,1,884 forks,规范与文档),GitHub API 实测于 2026-10-27. https://github.com/modelcontextprotocol/modelcontextprotocol
  20. keycloak/keycloak GitHub 仓库(37,546 星,9,105 forks,Java,Apache-2.0),GitHub API 实测于 2026-10-27. https://github.com/keycloak/keycloak
  21. zitadel/zitadel GitHub 仓库(15,452 星,1,406 forks,Go,AGPL-3.0),GitHub API 实测于 2026-10-27. https://github.com/zitadel/zitadel
  22. ory/hydra GitHub 仓库(17,634 星,1,617 forks,Go,Apache-2.0,描述为 OAuth2.1 provider),GitHub API 实测于 2026-10-27,最近推送 2026-07-29. https://github.com/ory/hydra
  23. ory/kratos GitHub 仓库(13,941 星,1,204 forks,Go,Apache-2.0,headless 认证基础设施),GitHub API 实测于 2026-10-27,最近推送 2026-07-29. https://github.com/ory/kratos
  24. panva/jose GitHub 仓库(7,848 星,378 forks,TypeScript,MIT,JOSE/JWT 实现),GitHub API 实测于 2026-10-27. https://github.com/panva/jose
  25. lestrrat-go/jwx GitHub 仓库(2,442 星,196 forks,Go,MIT,JWx 实现),GitHub API 实测于 2026-10-27. https://github.com/lestrrat-go/jwx
  26. 仓库路径三态核验(2026-10-27):spring-projects/spring-authorization、authorize/oidc-server、node-oidc-provider/oidc-provider、clerk/javascript-backend 均返回 HTTP 404,未入正文
  27. 授权服务器元数据 well-known 路径规范来源(RFC 8414 §3):默认路径 /.well-known/oauth-authorization-server,插入到 issuer 标识符的主机组件与路径组件之间,MUST 使用 https 方案
  28. 全站 body 三轮全量词频扫描(2026-10-27,718 篇 published,pitfall #159 分页参数 ?pageSize=50&page=N):委托授权象限关键词命中数——RFC 8693 为 1、DPoP 为 0、token exchange 为 0、confused deputy 为 0、PKCE 为 0、授权服务器为 0、scoped token 为 0、令牌轮换为 0
  29. 近邻文章语义核对(2026-10-27,实测读取 id=680 / 685 / 709 / 839 / 846 / 827 / 701 正文):id=709 仅在护栏九层表格中以"OAuth scope + 租户隔离"一行提及,id=685 仅在网关 JWT claim 解析语境中提及,id=680 仅在安全纵深一节提及 MCP 的 OAuth 2.1 资源服务器模式,id=839 讨论的是应用内部签发的能力令牌——均未展开委托授权协议
←返回文章列表

Related

可能也会喜欢

  • AI 应用交付物工程 2026:从工具结果到可验证产物10月31日
  • AI 应用的记忆写侧工程 2026:抽取、晋升与冲突消解10月30日
  • 向量数据库选型 2026:pgvector 与专用引擎的边界10月29日

Conversation

0 条

留下你的想法

加载评论中…

New comment