> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-zh.com/llms.txt
> Use this file to discover all available pages before exploring further.

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

* **状态（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_access`**：`offline_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 节](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-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 部署经验）
