SEP-2207:OIDC 风格的刷新令牌指南

SEP-2207:OIDC 风格的刷新令牌指南

社区译文;来源: MCP 29affa6b

本 SEP 已达到 Final 状态,并作为已接受设计的历史记录保留。 定稿后对协议所做的更改不会反映在此处。权威要求请参阅当前规范 及其变更日志。
字段值
SEP2207
标题OIDC 风格的刷新令牌指南
状态Final
类型标准跟踪
创建日期2026-02-04
作者Wils Dawson (@wdawson)
发起人Paul Carleton (@pcarleton)
PR#2207

摘要

本提案为 MCP 实现提供有关签发和请求刷新令牌的指导,尤其适用于授权服务器支持 offline_access scope 的情况。offline_access scope 源自 OIDC,但任何 OAuth 2.1 授权服务器都可以采用这一机制,让客户端明确请求刷新令牌。本 SEP 阐明授权服务器和 MCP 客户端使用该模式时的预期行为。

动机

MCP 的授权机制基于 OAuth 2.1,但许多实际部署使用的授权服务器还实现了 OpenID Connect(OIDC)。纯 OAuth 与 OIDC 的一个关键区别在于刷新令牌的处理方式:

  • 在纯 OAuth 2.1 中,没有让客户端明确请求刷新令牌的标准机制。授权服务器根据客户端能力(例如客户端元数据中的 refresh_token grant type)及其自身策略决定是否签发刷新令牌。
  • 在 OIDC(以及采用此约定的授权服务器)中,除 OAuth 的逻辑之外,还存在 offline_access scope,允许客户端明确请求刷新令牌。

这在 MCP 生态系统中造成了几个问题:

  1. 客户端没有请求刷新令牌:主要 MCP 客户端(Cursor、Claude、VS Code 等)没有通过 offline_access scope 明确请求刷新令牌,因为它们不知道授权服务器是否支持、期望或要求这一 scope。

  2. 资源服务器不应指定 offline_access:offline_access scope 不是资源特定的 scope,而是客户端与授权服务器之间的事项。将其包含在 WWW-Authenticate 标头的 scope 参数或受保护资源元数据的 scopes_supported 中,在语义上并不正确,因为这意味着资源要求刷新令牌,而资源永远不会提出这种要求。

  3. 授权服务器可能行为不一致:处理授权码授权时,不同授权服务器向不同客户端签发刷新令牌的行为可能不同,尤其是在客户端没有将 refresh_token 指定为 grant type,或没有请求 offline_access scope 的情况下。

  4. 互操作性缺口:缺少此指导时,各实现可能表现不一致,导致糟糕的用户体验(频繁重新认证)或安全问题(向无法安全存储刷新令牌的客户端签发令牌)。

规范

MCP 客户端要求

希望使用刷新令牌并且能够安全存储它们的 MCP 客户端 SHOULD 遵循以下指导:

  1. 声明能力:客户端 SHOULD 在其 grant_types 客户端元数据中包含 refresh_token,以表明其支持刷新令牌。

  2. 补充 scope:当客户端希望获得刷新令牌,并且授权服务器元数据的 scopes_supported 字段包含 offline_access 时,客户端在向授权服务器发出授权请求前,MAY 将 offline_access scope 加入来自资源服务器的 scope 列表。

  3. 不作保证:客户端 MUST NOT 假定声明支持能力或请求 offline_access 就一定会收到刷新令牌。授权服务器仍可根据其策略自行决定。

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

作为 OAuth 2.0 受保护资源的 MCP 服务器:

  1. SHOULD NOT 在 WWW-Authenticate 标头的 scope 参数中包含 offline_access,因为刷新令牌不是资源要求。

  2. SHOULD NOT 在受保护资源元数据的 scopes_supported 中包含 offline_access,因为它不是资源特定的 scope。

理由

为何不要求在 401 响应中包含 offline_access?

offline_access scope 与资源特定的 scope 有本质区别。它表达客户端希望获得长期访问,而不是资源提出的要求。根据 OAuth 2.1 第 5.3.1 节,WWW-Authenticate 中的 scope 属性表示“访问所请求资源时,访问令牌所需的 scope”。由于资源不要求 offline_access,将其包含在内在语义上并不正确。

为何检查客户端元数据中的 grant type?为何不总是签发刷新令牌?

OAuth 2.1 要求客户端注册其支持的 grant type。不支持 refresh_token grant 的客户端可能:

  • 无法安全存储刷新令牌
  • 没有使用刷新令牌的机制

向此类客户端签发刷新令牌会浪费授权服务器资源(跟踪永远不会使用的令牌),并且在令牌泄露时可能带来安全风险。

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

一些授权服务器只有在明确请求 offline_access 时才会签发刷新令牌,无论它们是完全符合 OIDC,还是仅采用这一约定。支持这种模式可为这类部署提供兼容路径。客户端可以检查授权服务器元数据的 scopes_supported 中是否存在 offline_access,从而检测支持此约定的授权服务器,并相应调整行为。

考虑过的替代方案

  1. 强制资源响应包含 offline_access:已否决,因为这会错误表达资源的要求,并形成反模式。

  2. 始终签发刷新令牌:已否决,因为这会忽略客户端能力和授权服务器安全策略。

  3. 制定单独的 OIDC 专用规范:已否决,选择采用同时适用于纯 OAuth 和 OIDC 部署的统一方法。

  4. 为授权服务器提供指导:已否决,选择依赖 OAuth 和 OIDC 规范提供相关指导,因为具体做法可能不同。

向后兼容性

本提案完全向后兼容:

  • 已经请求 offline_access 的客户端可继续工作
  • 已经检查客户端能力的授权服务器可继续工作
  • 不要求 MCP 服务器进行任何更改
  • 本指导是加法性的,不改变现有的必需行为

不遵循本指导的实现可能出现非最优行为(缺少刷新令牌或不必要地签发令牌),但仍能正常工作。

安全影响

正面安全影响

  1. 降低令牌泄露风险:不向未声明支持能力的客户端签发刷新令牌,可以降低长期令牌被不安全存储的风险。

  2. 纵深防御:风险评估步骤使授权服务器可以灵活实施额外的安全控制。

注意事项

  1. 客户端元数据可能并不充分:由于客户端元数据是自行声明的,恶意行为者可以注册一台声称支持 refresh_token grant 的客户端,以获取长期令牌。授权服务器 MAY 使用风险评估步骤(参见“规范”)施加额外限制,例如域名允许列表、信誉检查或验证要求,而不是在决定是否签发刷新令牌时仅依赖客户端元数据声明。

  2. Scope 注入:客户端添加 offline_access 时,应确保它不会干扰其他 scope 相关逻辑,或产生意外的授权提示。

参考实现

官方 MCP SDK 将提供展示本指导的参考实现:

  • TypeScript SDK:客户端 offline_access scope 处理
  • Python SDK:客户端 offline_access scope 处理
  • 授权服务器示例:演示如何检查客户端能力
  • 客户端一致性测试:便于验证 SDK 实现

SEP 被接受后将补充实现链接。

致谢

本提案通过 MCP Discord 的授权频道讨论形成,贡献者包括:

  • Aaron Parecki(OAuth/OIDC 专业知识)
  • Paul Carleton(MCP 授权指导)
  • Simon Russell(OIDC 部署经验)