Skip to main content
  • 状态(Status): Final
  • 类型(Type): Standards Track
  • 创建(Created): 2026-02-04
  • 作者(Author(s)): Wils Dawson (@wdawson)
  • 担保人(Sponsor): Paul Carleton (@pcarleton)
  • PR: #2207

摘要

本提案为 MCP 实现提供关于刷新令牌签发和请求的指南,尤其是在授权服务器支持 offline_access 作用域时。offline_access 作用域源自 OIDC,但任何 OAuth 2.1 授权服务器都可以采用它,作为让客户端显式请求刷新令牌的机制。本 SEP 澄清了授权服务器和 MCP 客户端在使用此模式时的预期行为。

动机

MCP 的授权机制基于 OAuth 2.1,但许多现实部署使用的授权服务器同时也实现了 OpenID Connect(OIDC)。纯 OAuth 与 OIDC 之间的一个关键差异在于刷新令牌的处理方式:
  • 纯 OAuth 2.1 中,没有标准机制让客户端显式请求刷新令牌。授权服务器基于客户端的能力(例如客户端元数据中的 refresh_token 许可类型)及其自身策略决定是否签发。
  • OIDC(以及采用此约定的授权服务器)中,除 OAuth 逻辑之外,还存在 offline_access 作用域,允许客户端显式请求刷新令牌。
这在 MCP 生态中造成了若干问题:
  1. 客户端未请求刷新令牌:主要的 MCP 客户端(Cursor、Claude、VS Code 等)没有通过 offline_access 作用域显式请求刷新令牌,因为它们不知道授权服务器是否支持、期望或要求它。
  2. 资源服务器不应指定 offline_accessoffline_access 作用域不是资源特定的作用域——它是客户端与授权服务器之间的关切。将其包含在 WWW-Authenticate 头部的 scope 参数或受保护资源元数据的 scopes_supported 中,在语义上是错误的,因为这暗示资源要求刷新令牌,而资源绝不会如此。
  3. 授权服务器可能不一致:在处理授权码许可时,不同的授权服务器在向不同客户端签发刷新令牌时可能有不同行为,尤其是当客户端未将 refresh_token 指定为许可类型或未请求 offline_access 作用域时。
  4. 互操作性缺口:没有本指南,各实现可能行为不一致,导致糟糕的用户体验(频繁重新认证)或安全问题(向无法安全存储刷新令牌的客户端签发刷新令牌)。

规范

MCP 客户端要求

打算使用刷新令牌且有能力安全存储它们的 MCP 客户端**应当(SHOULD)**遵循以下指南:
  1. 公告能力:客户端**应当(SHOULD)**在其 grant_types 客户端元数据中包含 refresh_token,以表明它们支持刷新令牌。
  2. 作用域增补:当客户端希望获得刷新令牌且授权服务器元数据在其 scopes_supported 字段中包含 offline_access 时,客户端**可以(MAY)**在向授权服务器发起授权请求之前,将 offline_access 作用域添加到来自资源服务器的作用域列表中。
  3. 无保证:客户端**不得(MUST NOT)**假定公告支持或请求 offline_access 就能保证收到刷新令牌。授权服务器基于其策略保留自由裁量权。

MCP 服务器(资源服务器)要求

MCP 服务器(作为 OAuth 2.0 受保护资源):
  1. **不应当(SHOULD NOT)**在 WWW-Authenticate 头部的 scope 参数中包含 offline_access,因为刷新令牌不是资源要求。
  2. **不应当(SHOULD NOT)**在受保护资源元数据的 scopes_supported 中包含 offline_access,因为它不是资源特定的作用域。

理由

为何不在 401 响应中要求 offline_access

offline_access 作用域与资源特定的作用域有本质不同。它代表客户端对长期访问的意愿,而非资源的要求。依据 OAuth 2.1 第 5.3.1 节WWW-Authenticate 中的 scope 属性表示”访问所请求资源所需的访问令牌作用域”。由于资源不要求 offline_access,将其包含进去在语义上是错误的。

为何检查客户端元数据中的许可类型?为何不总是签发刷新令牌?

OAuth 2.1 要求客户端注册其所支持的许可类型。不支持 refresh_token 许可的客户端要么:
  • 无法安全存储刷新令牌
  • 没有使用它们的机制
向此类客户端签发刷新令牌会浪费授权服务器资源(跟踪永不会被使用的令牌),并且若令牌泄露可能带来安全风险。

为何允许 offline_access 作为替代信号?

一些授权服务器——无论是完全符合 OIDC 还是仅采用此约定——只在显式请求 offline_access 时才签发刷新令牌。支持此模式为此类部署提供了一条兼容路径。客户端可以通过检查授权服务器元数据的 scopes_supported 中是否有 offline_access,来检测支持此约定的授权服务器,并相应地调整其行为。

所考虑的替代方案

  1. 在资源响应中强制 offline_access:被否决,因为它歪曲了资源的要求并造成反模式。
  2. 总是签发刷新令牌:被否决,因为它忽视了客户端能力和授权服务器的安全策略。
  3. 单独的 OIDC 专用规范:被否决,转而采用一种对纯 OAuth 和 OIDC 部署都适用的统一方式。
  4. 为授权服务器提供指南:被否决,转而依赖 OAuth 和 OIDC 规范来提供这一指南,因为它可能各有不同。

向后兼容性

本提案完全向后兼容:
  • 已经请求 offline_access 的客户端继续有效
  • 已经检查客户端能力的授权服务器继续有效
  • MCP 服务器无需作任何更改
  • 该指南是增量的,不改变既有的必需行为
不遵循本指南的实现可能会经历次优行为(缺少刷新令牌或不必要的令牌签发),但仍将保持功能正常。

安全影响

正面安全影响

  1. 降低令牌泄露风险:通过不向未公告支持的客户端签发刷新令牌,我们降低了长期令牌被不安全存储的风险。
  2. 纵深防御:基于风险的评估步骤给予授权服务器实施额外安全控制的灵活性。

考量

  1. 客户端元数据可能不够:由于客户端元数据是自我申报的,恶意行为者可能注册一个声称支持 refresh_token 许可的客户端以获取长期令牌。授权服务器在决定是否签发刷新令牌时,**可以(MAY)**使用基于风险的评估步骤(见规范)来施加额外限制——例如域名允许列表、信誉检查或验证要求——而非仅仅依赖客户端元数据的声明。
  2. 作用域注入:添加 offline_access 的客户端应确保这不干扰其他与作用域相关的逻辑,或造成意外的授权提示。

参考实现

演示本指南的参考实现将在官方 MCP SDK 中提供:
  • TypeScript SDK:客户端侧的 offline_access 作用域处理
  • Python SDK:客户端侧的 offline_access 作用域处理
  • 授权服务器示例:客户端能力检查的演示
  • 客户端合规测试:便于对 SDK 实现进行验证
实现的链接将在本 SEP 被接受后添加。

致谢

本提案通过 MCP Discord 授权频道的讨论发展而来,得到以下人士的意见:
  • Aaron Parecki(OAuth/OIDC 专业知识)
  • Paul Carleton(MCP 授权指南)
  • Simon Russell(OIDC 部署经验)