已接受标准轨道
| 字段 | 值 |
|---|---|
| SEP | 2207 |
| 标题 | OIDC 风格刷新令牌指南 |
| 状态 | 已接受 |
| 类型 | 标准轨道 |
| 创建日期 | 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授予类型)及其自身的策略来决定是否颁发刷新令牌。 - 在OIDC(以及采用此约定的授权服务器)中,存在
offline_accessscope 以允许客户端显式请求刷新令牌,作为 OAuth 逻辑的补充。
-
客户端未请求刷新令牌:主要 MCP 客户端(Cursor、Claude、VS Code 等)未通过
offline_accessscope 显式请求刷新令牌,因为它们不知道授权服务器是否支持、期望或需要它。 -
资源服务器不应指定
offline_access:offline_accessscope 不是特定于资源的 scope——它是客户端和授权服务器之间的问题。将其包含在WWW-Authenticate头部的scope参数中或受保护资源元数据的scopes_supported中在语义上是不正确的,因为它暗示资源_需要_刷新令牌,而实际上永远不会需要。 -
授权服务器可能不一致:在处理授权码授予时,不同的授权服务器在向不同客户端颁发刷新令牌时可能有不同的行为,特别是当客户端未指定
refresh_token作为授予类型或未请求offline_accessscope 时。 - 互操作性差距:如果没有此指南,实现的行为可能不一致,导致用户体验不佳(频繁重新认证)或安全问题(向无法安全存储它们的客户端颁发刷新令牌)。
规范
MCP 客户端要求
打算使用刷新令牌且能够安全存储它们的 MCP 客户端应该遵循以下指南:-
声明能力:客户端应该在其
grant_types客户端元数据中包含refresh_token,以表明它们支持刷新令牌。 -
Scope 增强:当客户端需要刷新令牌且授权服务器元数据的
scopes_supported字段中包含offline_access时,客户端可以在向授权服务器发出授权请求之前,将offline_accessscope 添加到资源服务器的 scope 列表中。 -
无保证:客户端不得假设声明支持或请求
offline_access就能保证收到刷新令牌。授权服务器保留根据其策略进行裁量的权利。
MCP 服务器(资源服务器)要求
MCP 服务器(作为 OAuth 2.0 受保护资源):-
不应该在
WWW-Authenticate头部的scope参数中包含offline_access,因为刷新令牌不是资源要求。 -
不应该在受保护资源元数据的
scopes_supported中包含offline_access,因为它不是特定于资源的 scope。
理由
为什么不在 401 响应中要求 offline_access?
offline_access scope 与特定于资源的 scope fundamentally 不同。它代表客户端对长期访问的愿望,而不是资源的要求。根据 OAuth 2.1 第 5.3.1 节,WWW-Authenticate 中的 scope 属性表示“访问请求资源所需的访问令牌 scope”。由于资源不需要 offline_access,包含它在语义上是不正确的。
为什么要检查客户端元数据中的授予类型?为什么不总是颁发刷新令牌?
OAuth 2.1 要求客户端注册其支持的授予类型。不支持refresh_token 授予的客户端:
- 无法安全存储刷新令牌
- 没有使用它们的机制
为什么允许 offline_access 作为替代信号?
一些授权服务器(无论是完全符合 OIDC 还是仅采用此约定)仅在显式请求 offline_access 时才颁发刷新令牌。支持此模式为此类部署提供了兼容路径。客户端可以通过检查授权服务器元数据中的 scopes_supported 是否包含 offline_access 来检测支持此约定的授权服务器,并相应地调整其行为。
考虑的替代方法
-
在资源响应中强制要求
offline_access:被拒绝,因为它歪曲了资源的要求并创建了反模式。 - 总是颁发刷新令牌:被拒绝,因为它忽略了客户端能力和授权服务器安全策略。
- 单独的 OIDC 特定规范:被拒绝,倾向于采用适用于纯 OAuth 和 OIDC 部署的统一方法。
- 为授权服务器提供指南:被拒绝,倾向于依赖 OAuth 和 OIDC 规范作为此指南,因为它可能有所不同。
向后兼容性
本提案完全向后兼容:- 已经请求
offline_access的客户端继续工作 - 已经检查客户端能力的授权服务器继续工作
- MCP 服务器不需要进行任何更改
- 指南是附加的,不改变现有的必需行为
安全影响
积极的安全影响
- 降低令牌泄露风险:通过不向未声明支持的客户端颁发刷新令牌,我们降低了长期令牌被不安全存储的风险。
- 深度防御:基于风险的评估步骤使授权服务器能够灵活地实施额外的安全控制。
注意事项
-
客户端元数据可能不足:由于客户端元数据是自我报告的,恶意行为者可以注册声称支持
refresh_token授予的客户端以获取长期令牌。授权服务器可以使用基于风险的评估步骤(参见规范)来应用额外的限制——例如域名允许列表、信誉检查或验证要求——而不是在决定是否颁发刷新令牌时仅依赖客户端元数据声明。 -
Scope 注入:添加
offline_access的客户端应确保这不会干扰其他 scope 相关逻辑或创建意外的授权提示。
参考实现
展示此指南的参考实现将在官方 MCP SDK 中提供:- TypeScript SDK:客户端侧
offline_accessscope 处理 - Python SDK:客户端侧
offline_accessscope 处理 - 授权服务器示例:客户端能力检查演示
- 客户端一致性测试:允许轻松验证 SDK 实现
致谢
本提案是通过 MCP Discord 的授权频道讨论开发的,输入来自:- Aaron Parecki (OAuth/OIDC 专业知识)
- Paul Carleton (MCP 授权指南)
- Simon Russell (OIDC 部署经验)