授权
1. 介绍
1.1 目的和范围
模型上下文协议在传输层提供授权功能,使 MCP 客户端能够代表资源所有者向受限的 MCP 服务器发出请求。本规范定义了基于 HTTP 的传输的授权流程。
1.2 协议要求
授权对 MCP 实现是可选的。当支持时:
- 使用基于 HTTP 的传输的实现应该遵守本规范。
- 使用 STDIO 传输的实现不应该遵循本规范,而应从环境中检索凭据。
- 使用其他传输的实现必须遵循其协议的既定安全最佳实践。
2. 授权流程
2.1 角色定义
受保护的 MCP 服务器 充当 OAuth 2.1 资源服务器,能够使用访问令牌接受和响应受保护资源请求。
MCP 客户端 充当 OAuth 2.1 客户端,代表资源所有者发出受保护资源请求。
授权服务器 负责与用户交互(如有必要)并颁发用于 MCP 服务器的访问令牌。授权服务器的实现细节超出了本规范的范围。它可以与资源服务器一起托管或作为单独的实体。授权服务器发现部分指定了 MCP 服务器如何向客户端指示其相应授权服务器的位置。
2.2 概述
授权服务器必须为机密和公共客户端实现具有适当安全措施的 OAuth 2.1。
授权服务器和 MCP 客户端应该支持 OAuth 客户端 ID 元数据文档 (draft-ietf-oauth-client-id-metadata-document-00)。
授权服务器和 MCP 客户端可以支持 OAuth 2.0 动态客户端注册协议 (RFC7591)。
MCP 服务器必须实现 OAuth 2.0 受保护资源元数据 (RFC9728)。MCP 客户端必须使用 OAuth 2.0 受保护资源元数据进行授权服务器发现。
MCP 授权服务器必须提供以下至少一种发现机制:
- OAuth 2.0 授权服务器元数据 (RFC8414)
- OpenID Connect Discovery 1.0
MCP 客户端必须支持这两种发现机制,以获取与授权服务器交互所需的信息。
2.2 基本 OAuth 2.1 授权
当需要授权且客户端尚未证明时,服务器必须响应 HTTP 401 未授权。
客户端在收到 HTTP 401 未授权 后启动 OAuth 2.1 IETF 草案 授权流程。
以下演示了使用 PKCE 的公共客户端的基本 OAuth 2.1。
sequenceDiagram
participant B as 用户代理(浏览器)
participant C as 客户端
participant M as MCP 服务器
C->>M: MCP 请求
M->>C: HTTP 401 未授权
Note over C: 生成 code_verifier 和 code_challenge
C->>B: 打开带有授权 URL + code_challenge 的浏览器
B->>M: GET /authorize
Note over M: 用户登录并授权
M->>B: 重定向到回调 URL 并带有授权码
B->>C: 带有授权码的回调
C->>M: 带有 code + code_verifier 的令牌请求
M->>C: 访问令牌(+ 刷新令牌)
C->>M: 带有访问令牌的 MCP 请求
Note over C,M: 开始标准 MCP 消息交换
2.3 授权服务器发现
本节描述 MCP 服务器向 MCP 客户端公开其关联授权服务器的机制,以及 MCP 客户端可以确定授权服务器端点和支持功能的发现过程。
2.3.1 授权服务器位置
MCP 服务器必须实现 OAuth 2.0 受保护资源元数据 (RFC9728) 规范来指示授权服务器的位置。MCP 服务器返回的受保护资源元数据文档必须包含 authorization_servers 字段,其中至少包含一个授权服务器。
authorization_servers 的具体使用超出了本规范的范围;实现者应查阅 OAuth 2.0 受保护资源元数据 (RFC9728) 以获取实现细节指导。
实现者应注意,受保护资源元数据文档可以定义多个授权服务器。选择使用哪个授权服务器的责任在于 MCP 客户端,遵循 RFC9728 第 7.6 节"授权服务器" 中指定的指南。
2.3.2 受保护资源元数据发现要求
MCP 服务器必须实现以下发现机制之一,以向 MCP 客户端提供授权服务器位置信息:
WWW-Authenticate 头部:在返回
401 Unauthorized响应时,在WWW-AuthenticateHTTP 头部的resource_metadata下包含资源元数据 URL,如 RFC9728 第 5.1 节 所述。Well-Known URI:在 RFC9728 指定的 well-known URI 提供元数据。这可以是:
- 在服务器的 MCP 端点路径:
https://example.com/public/mcp可以在https://example.com/.well-known/oauth-protected-resource/public/mcp托管元数据 - 在根路径:
https://example.com/.well-known/oauth-protected-resource
- 在服务器的 MCP 端点路径:
MCP 客户端必须支持这两种发现机制,当存在时使用解析的 WWW-Authenticate 头部中的资源元数据 URL;否则,它们必须回退到按上述顺序构造和请求 well-known URI。
MCP 服务器应该在 WWW-Authenticate 头部中包含 scope 参数,如 RFC 6750 第 3 节 中定义,以指示访问资源所需的 scope。这为客户端提供了关于在授权期间请求的适当 scope 的即时指导,遵循最小权限原则并防止客户端请求过多权限。
WWW-Authenticate 挑战中包含的 scope 可以匹配 scopes_supported,是其子集或超集,或者是既不是严格子集也不是超集的替代集合。客户端不得假定挑战的 scope 集合与 scopes_supported 之间存在任何特定的集合关系。客户端必须将挑战中提供的 scope 视为满足当前请求的权威。服务器应该努力保持它们如何构造 scope 集的一致性,但它们不需要通过 scopes_supported 公开每个动态颁发的 scope。
带有 scope 指导的 401 响应示例:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="files:read"MCP 客户端必须能够解析 WWW-Authenticate 头部并适当响应来自 MCP 服务器的 HTTP 401 Unauthorized 响应。
如果 scope 参数不存在,客户端应该应用 Scope 选择策略 部分中定义的回退行为。
2.3.3 授权服务器元数据发现
为了处理不同的 issuer URL 格式并确保与 OAuth 2.0 授权服务器元数据和 OpenID Connect Discovery 1.0 规范的互操作性,MCP 客户端在发现授权服务器元数据时必须尝试多个 well-known 端点。
发现方法基于 RFC8414 第 3.1 节"授权服务器元数据请求" 用于 OAuth 2.0 授权服务器元数据发现,以及 RFC8414 第 5 节"兼容性说明" 用于 OpenID Connect Discovery 1.0 互操作性。
对于带有路径组件的 issuer URL(例如 https://auth.example.com/tenant1),客户端必须按以下优先级顺序尝试端点:
- 带路径插入的 OAuth 2.0 授权服务器元数据:
https://auth.example.com/.well-known/oauth-authorization-server/tenant1 - 带路径插入的 OpenID Connect Discovery 1.0:
https://auth.example.com/.well-known/openid-configuration/tenant1 - 路径追加的 OpenID Connect Discovery 1.0:
https://auth.example.com/tenant1/.well-known/openid-configuration
对于没有路径组件的 issuer URL(例如 https://auth.example.com),客户端必须尝试:
- OAuth 2.0 授权服务器元数据:
https://auth.example.com/.well-known/oauth-authorization-server - OpenID Connect Discovery 1.0:
https://auth.example.com/.well-known/openid-configuration
2.3.4 序列图
以下图表概述了示例流程:
sequenceDiagram
participant C as MCP 客户端
participant M as MCP 服务器
participant A as 授权服务器
C->>M: MCP 请求(无令牌)
M->>C: 401 Unauthorized + WWW-Authenticate 头部
Note over M,C: resource_metadata URL + scope
C->>M: GET /.well-known/oauth-protected-resource
M->>C: 受保护资源元数据 + authorization_servers
C->>A: GET /.well-known/oauth-authorization-server
A->>C: 授权服务器元数据
Note over C: 发现流程完成
Note over C: 继续 OAuth 2.1 授权
完整的授权流程示例:
sequenceDiagram
participant U as 用户
participant C as MCP 客户端
participant B as 浏览器
participant M as MCP 服务器
participant A as 授权服务器
C->>M: MCP 请求
M->>C: 401 + WWW-Authenticate
C->>M: 获取受保护资源元数据
M->>C: authorization_servers + scopes_supported
C->>A: 获取授权服务器元数据
A->>C: 授权端点 + 令牌端点
Note over C: 生成 PKCE 参数
C->>B: 打开授权 URL
B->>A: 授权请求
A->>U: 显示同意界面
U->>A: 批准访问
A->>B: 重定向 + 授权码
B->>C: 授权码
C->>A: 令牌请求 + PKCE 验证器
A->>C: 访问令牌 + 刷新令牌
C->>M: MCP 请求 + Bearer 令牌
M->>C: 受保护资源
2.3.5 授权基础 URL
授权基础 URL 必须通过丢弃 MCP 端点 URL 的任何现有 path 组件来确定。例如:
如果 SSE 端点是 https://api.example.com/v1/sse,那么:
- 授权基础 URL 是
https://api.example.com - 元数据端点必须位于
https://api.example.com/.well-known/oauth-authorization-server
这确保授权端点始终位于提供 SSE 端点的域的根级别,无论 SSE 端点 URL 中的任何路径组件如何。
2.3.3 没有元数据发现的服务器的回退
对于不实现 OAuth 2.0 授权服务器元数据的服务器,客户端必须使用以下相对于授权基础 URL 的默认端点路径(如 第 2.3.2 节 中所定义):
| 端点 | 默认路径 | 描述 |
|---|---|---|
| 授权端点 | /authorize | 用于授权请求 |
| 令牌端点 | /token | 用于令牌交换和刷新 |
| 注册端点 | /register | 用于动态客户端注册 |
例如,对于 SSE 端点 https://api.example.com/v1/sse,默认端点将是:
https://api.example.com/authorizehttps://api.example.com/tokenhttps://api.example.com/register
客户端必须首先尝试通过元数据文档发现端点,然后再回退到默认路径。使用默认路径时,所有其他协议要求保持不变。
2.4 客户端注册方式
MCP 支持三种客户端注册机制。根据场景选择:
- 客户端 ID 元数据文档:当客户端和服务器没有预先关系时(最常见)
- 预注册:当客户端和服务器有现有关系时
- 动态客户端注册:用于向后兼容或特定需求
支持所有选项的客户端应该遵循以下优先级顺序:
- 如果客户端有预注册的客户端信息,则使用该信息
- 如果授权服务器指示支持客户端 ID 元数据文档(通过 OAuth 授权服务器元数据中的
client_id_metadata_document_supported),则使用客户端 ID 元数据文档 - 如果授权服务器支持动态客户端注册(通过 OAuth 授权服务器元数据中的
registration_endpoint),则使用动态客户端注册 - 如果没有其他选项可用,则提示用户输入客户端信息
2.4.1 客户端 ID 元数据文档
MCP 客户端和授权服务器应该支持 OAuth 客户端 ID 元数据文档中规定的 OAuth 客户端 ID 元数据文档。这种方法使客户端能够使用 HTTPS URL 作为客户端标识符,其中 URL 指向包含客户端元数据的 JSON 文档。这解决了服务器和客户端没有预先存在的关系的常见 MCP 场景。
实现要求
支持客户端 ID 元数据文档的 MCP 实现必须遵循 OAuth 客户端 ID 元数据文档中规定的要求。关键要求包括:
对于 MCP 客户端:
- 客户端必须在遵循 RFC 要求的 HTTPS URL 上托管其元数据文档
client_idURL 必须使用 “https” scheme 并包含路径组件,例如https://example.com/client.json- 元数据文档必须至少包含以下属性:
client_id、client_name、redirect_uris - 客户端必须确保元数据中的
client_id值与文档 URL 完全匹配 - 客户端可以使用
private_key_jwt进行客户端身份验证(例如,对于令牌端点的请求),并按照客户端 ID 元数据文档第 6.2 节中的描述进行适当的 JWKS 配置
对于授权服务器:
- 应该在遇到 URL 格式的 client_ids 时获取元数据文档
- 必须验证获取的文档的
client_id与 URL 完全匹配 - 应该缓存元数据,遵守 HTTP 缓存头
- 必须根据元数据文档中列出的 URI 验证授权请求中呈现的重定向 URI
- 必须验证文档结构是有效的 JSON 并包含必填字段
- 应该遵循客户端 ID 元数据文档第 6 节中的安全考虑
示例元数据文档
{
"client_id": "https://app.example.com/oauth/client-metadata.json",
"client_name": "Example MCP Client",
"client_uri": "https://app.example.com",
"logo_uri": "https://app.example.com/logo.png",
"redirect_uris": [
"http://127.0.0.1:3000/callback",
"http://localhost:3000/callback"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}客户端 ID 元数据文档流程
下图说明了使用客户端 ID 元数据文档时的完整流程:
sequenceDiagram
participant User as 用户
participant Client as MCP 客户端
participant Server as 授权服务器
participant Metadata as 元数据端点<br/>(客户端的 HTTPS URL)
participant Resource as MCP 服务器
Note over Client,Metadata: 客户端在<br/>https://app.example.com/oauth/metadata.json 托管元数据
User->>Client: 发起到 MCP 服务器的连接
Client->>Server: 授权请求<br/>client_id=https://app.example.com/oauth/metadata.json<br/>redirect_uri=http://localhost:3000/callback
Server->>User: 身份验证提示
User->>Server: 提供凭据
Note over Server: 验证用户
Note over Server: 检测到 URL 格式的 client_id
Server->>Metadata: GET https://app.example.com/oauth/metadata.json
Metadata-->>Server: JSON 元数据文档<br/>{client_id, client_name, redirect_uris, ...}
Note over Server: 验证:<br/>1. client_id 与 URL 匹配<br/>2. redirect_uri 在允许列表中<br/>3. 文档结构有效<br/>4. (可选) 域通过信任策略允许
alt 验证成功
Server->>User: 显示带有 client_name 的同意页面
User->>Server: 批准访问
Server->>Client: 通过 redirect_uri 的授权码
Client->>Server: 用码交换令牌<br/>client_id=https://app.example.com/oauth/metadata.json
Server-->>Client: 访问令牌
Client->>Resource: 使用访问令牌的 MCP 请求
Resource-->>Client: MCP 响应
else 验证失败
Server->>User: 错误响应<br/>error=invalid_client 或 invalid_request
end
Note over Server: 为将来的请求缓存元数据<br/>(遵守 HTTP 缓存头)
发现
授权服务器通过在其 OAuth 授权服务器元数据中包含以下属性来宣传它们支持使用客户端 ID 元数据文档的客户端:
{
"client_id_metadata_document_supported": true
}MCP 客户端应该检查此功能,如果不可用可以回退到动态客户端注册或预注册。
2.4.2 预注册
MCP 客户端应该支持静态客户端凭据的选项,例如通过预注册流程提供的凭据。这可以是:
- 专门为该 MCP 客户端硬编码客户端 ID(以及适用的情况下,客户端凭据),以便在与该授权服务器交互时使用,或
- 向用户呈现一个 UI,允许他们在自己注册 OAuth 客户端后输入这些详细信息(例如,通过服务器托管的配置界面)。
2.4.3 动态客户端注册
MCP 客户端和授权服务器可以支持 OAuth 2.0 动态客户端注册协议 RFC7591,以允许 MCP 客户端在无需用户交互的情况下获取 OAuth 客户端 ID。此选项包含在内是为了与早期版本的 MCP 授权规范向后兼容。
2.5 动态客户端注册
MCP 客户端和授权服务器应该支持 OAuth 2.0 动态客户端注册协议 (RFC7591),以允许 MCP 客户端在无需用户交互的情况下获取 OAuth 客户端 ID。这为客户端提供了一种标准化的方式,以便自动向新的授权服务器注册,这对 MCP 至关重要,因为:
- 客户端可能事先不知道所有可能的 MCP 服务器及其授权服务器
- 手动注册会给用户带来摩擦
- 它使无缝连接到新 MCP 服务器及其授权服务器成为可能
- 授权服务器可以实现自己的注册策略
任何_不_支持动态客户端注册的授权服务器需要提供获取客户端 ID(以及适用的情况下,客户端凭据)的替代方式。对于这些授权服务器,MCP 客户端必须:
- 专门为该 MCP 客户端硬编码客户端 ID(以及适用的情况下,客户端凭据),以便在与该授权服务器交互时使用,或
- 向用户呈现一个 UI,允许他们在自己注册 OAuth 客户端后输入这些详细信息(例如,通过服务器托管的配置界面)。
2.6.1 Scope 选择策略
在实现授权流程时,MCP 客户端应该遵循最小权限原则,仅请求其预期操作所需的 scope。在初始授权握手期间,MCP 客户端应该按照以下优先级顺序选择 scope:
- 使用
scope参数:从 401 响应中的初始WWW-Authenticate头部中提供(如果提供) - 如果
scope不可用:使用受保护资源元数据文档中scopes_supported定义的所有 scope,如果scopes_supported未定义则省略scope参数
这种方法适应了 MCP 客户端的通用性质,它们通常缺乏特定领域的知识来对单个 scope 选择做出明智的决定。请求所有可用的 scope 允许授权服务器和最终用户在同意过程中确定适当的权限。
这种方法在遵循最小权限原则的同时最大限度地减少了用户摩擦。scopes_supported 字段旨在表示基本功能所需的最小 scope 集合(参见Scope 最小化),通过分步授权流程步骤逐步请求额外的 scope。
2.7 授权流程步骤
完整的授权流程如下所示:
sequenceDiagram
participant B as 用户代理(浏览器)
participant C as 客户端
participant M as MCP 服务器
C->>M: GET /.well-known/oauth-authorization-server
alt 服务器支持发现
M->>C: 授权服务器元数据
else 无发现
M->>C: 404(使用默认端点)
end
alt 动态客户端注册
C->>M: POST /register
M->>C: 客户端凭据
end
Note over C: 生成 PKCE 参数
C->>B: 打开带有授权 URL + code_challenge 的浏览器
B->>M: 授权请求
Note over M: 用户授权
M->>B: 重定向到回调,带有授权码
B->>C: 授权码回调
C->>M: 令牌请求 + code_verifier
M->>C: 访问令牌(+ 刷新令牌)
C->>M: 带有访问令牌的 API 请求
2.7.1 决策流程概述
flowchart TD
A[开始授权流程] --> B{检查元数据发现}
B -->|可用| C[使用元数据端点]
B -->|不可用| D[使用默认端点]
C --> G{检查注册端点}
D --> G
G -->|可用| H[执行动态注册]
G -->|不可用| I[需要替代注册]
H --> J[开始 OAuth 流程]
I --> J
J --> K[生成 PKCE 参数]
K --> L[请求授权]
L --> M[用户授权]
M --> N[用码交换令牌]
N --> O[使用访问令牌]
2.8 Scope 挑战处理
当客户端使用当前 scope 发出请求但服务器确定需要额外权限时,服务器必须使用 HTTP 403 Forbidden 响应并在 WWW-Authenticate 头部中包含 insufficient_scope 错误和所需的 scope 参数进行响应。
Scope 挑战响应示例:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="files:read files:write",
error_description="需要写入权限才能修改文件"收到 scope 挑战时,客户端应该:
- 检查错误是否为
insufficient_scope - 提取所需的
scope参数 - 使用所需 scope 启动新的授权流程
- 在用户同意后使用新令牌重试原始请求
这种"分步授权"方法允许客户端最初请求最小权限,仅在需要时才请求额外的 scope,遵循最小权限原则同时保持流畅的用户体验。
2.9 访问令牌使用
2.6.1 令牌要求
访问令牌处理必须符合 OAuth 2.1 第 5 节 对资源请求的要求。具体来说:
- MCP 客户端必须使用授权请求头字段 第 5.1.1 节:
Authorization: Bearer <access-token>- 访问令牌不得包含在 URI 查询字符串中
请求示例:
GET /mcp/v1/messages HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json2.9.2 令牌验证和处理
MCP 服务器(资源服务器)必须按照 OAuth 2.1 第 5.2 节 中所述验证访问令牌。如果验证失败,服务器必须根据 第 5.3 节 错误处理要求进行响应:
- 无效或过期的令牌必须收到 HTTP 401 Unauthorized 响应
- Scope 不足必须收到 HTTP 403 Forbidden 响应,包含
insufficient_scope错误
2.9.3 令牌刷新
当访问令牌过期时,客户端应该使用刷新令牌(如果可用)自动获取新的访问令牌,而无需用户交互。令牌刷新必须遵循 OAuth 2.1 第 4.3 节 中指定的刷新令牌授权类型。
令牌刷新请求示例:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3如果刷新令牌无效或过期,客户端必须通过重新启动完整的授权流程来重新进行身份验证。
3. 资源指示器
MCP 客户端必须实现 RFC 8707 中定义的 OAuth 2.0 资源指示器,以明确指定请求令牌的目标资源。resource 参数:
- 必须包含在授权请求和令牌请求中
- 必须标识客户端打算使用令牌访问的 MCP 服务器
- 必须使用 RFC 8707 第 2 节 中定义的 MCP 服务器的规范 URI
3.1 规范服务器 URI
在本规范中,MCP 服务器的规范 URI 定义为 RFC 8707 第 2 节 中指定的资源标识符,并与 RFC 9728 中的 resource 参数保持一致。
MCP 客户端应该遵循 RFC 8707 中的指导,为它们打算访问的 MCP 服务器提供最具体的 URI。虽然规范形式使用小写的 scheme 和 host 组件,但实现应该接受大写的 scheme 和 host 组件以提高健壮性和互操作性。
有效规范 URI 示例:
https://mcp.example.com/mcphttps://mcp.example.comhttps://mcp.example.com:8443https://mcp.example.com/server/mcp(当需要路径组件来标识单个 MCP 服务器时)
无效规范 URI 示例:
mcp.example.com(缺少 scheme)https://mcp.example.com#fragment(包含片段)
注意:虽然
https://mcp.example.com/(带尾部斜杠)和https://mcp.example.com(不带尾部斜杠)根据 RFC 3986 在技术上都是有效的绝对 URI,但实现应该始终使用不带尾部斜杠的形式以获得更好的互操作性,除非尾部斜杠对特定资源具有语义重要性。
例如,如果访问 https://mcp.example.com 的 MCP 服务器,授权请求将包含:
&resource=https%3A%2F%2Fmcp.example.comMCP 客户端必须发送此参数,无论授权服务器是否支持它。
4. 错误处理
服务器必须为授权错误返回适当的 HTTP 状态码:
| 状态码 | 描述 | 使用场景 |
|---|---|---|
| 401 | Unauthorized | 需要授权或令牌无效 |
| 403 | Forbidden | Scope 无效或权限不足 |
| 400 | Bad Request | 授权请求格式错误 |
4.1 Scope 挑战的详细处理
本节涵盖在运行时操作期间客户端已拥有令牌但需要额外权限时处理 scope 不足错误。这遵循 OAuth 2.1 第 5 节 中定义的错误处理模式,并利用 RFC 9728 中的元数据字段。
4.1.1 运行时 Scope 不足错误
当客户端在运行时操作期间使用 scope 不足的访问令牌发出请求时,服务器应该响应:
HTTP 403 Forbidden状态码(根据 RFC 6750 第 3.1 节)WWW-Authenticate头部,包含Bearerscheme 和额外参数:error="insufficient_scope"- 指示授权失败的具体类型scope="required_scope1 required_scope2"- 指定操作所需的最小 scoperesource_metadata- 受保护资源元数据文档的 URI(与 401 响应保持一致)error_description(可选)- 错误的人类可读描述
服务器 Scope 管理:当响应 scope 不足错误时,服务器应该在 scope 参数中包含满足当前请求所需的 scope。
服务器在确定包含哪些 scope 时有以下灵活性:
- 最小方法:仅包含特定操作所需的新 scope。如果现有已授权的 scope 是必需的,也应包含它们,以防止客户端失去先前授权的权限
- 推荐方法:同时包含现有相关 scope 和新所需的 scope,以防止客户端失去先前授权的权限
- 扩展方法:包含现有 scope、新所需的 scope 以及通常一起使用的相关 scope
选择取决于服务器对用户体验影响和授权摩擦的评估。服务器应该在其 scope 包含策略中保持一致,以为客户端提供可预测的行为。
Scope 不足响应示例:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="files:read files:write user:profile",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
error_description="需要额外的文件写入权限"4.1.2 分步授权流程
客户端将在初始授权期间或运行时(insufficient_scope)收到与 scope 相关的错误。客户端应该通过分步授权流程请求具有增加的 scope 集的新访问令牌来响应这些错误,或以其他适当的方式处理错误。
代表用户行事的客户端应该尝试分步授权流程。代表自身行事的客户端(client_credentials 客户端)可以尝试分步授权流程或立即中止请求。
流程如下:
- 解析错误信息:从授权服务器响应或
WWW-Authenticate头部解析 - 确定所需 scope:如 2.6.1 Scope 选择策略 中所述
- 启动(重新)授权:使用确定的 scope 集
- 重试原始请求:使用新授权,但重试次数不超过几次,将此视为永久授权失败
客户端应该实施重试限制,并应该跟踪 scope 升级尝试,以避免对相同资源和操作组合的重复失败。
5. 安全考虑
实现必须遵循 OAuth 2.1 第 7 节"安全考虑" 中规定的 OAuth 2.1 安全最佳实践。
5.1 关键安全要求
必须实施以下安全要求:
- PKCE 要求:所有客户端必须使用带有 SHA-256 的 PKCE
- HTTPS 要求:所有授权端点必须通过 HTTPS 提供服务
- 令牌存储:客户端必须按照 OAuth 2.1 最佳实践安全存储令牌
- 令牌轮换:服务器应该强制令牌过期和轮换
- 重定向 URI 验证:服务器必须验证重定向 URI,以防止开放重定向漏洞
- 重定向 URI 限制:重定向 URI 必须是 localhost URL 或 HTTPS URL
- 令牌范围验证:MCP 服务器必须验证访问令牌是专门为它们作为预期受众签发的
- 令牌隔离:MCP 客户端不得向 MCP 服务器发送除由 MCP 服务器的授权服务器签发的令牌之外的令牌
- 令牌验证:授权服务器必须仅接受对其自己资源有效的令牌
- 令牌生命周期:令牌生命周期应该根据安全要求限制
5.2 安全最佳实践
除了强制性要求外,实现应该考虑:
- 实施速率限制以防止滥用
- 记录所有授权尝试以用于审计
- 使用短期访问令牌和长期刷新令牌
- 实施令牌撤销机制
- 定期审查和更新授权策略
- 监控异常授权模式
5.3 令牌受众绑定和验证
RFC 8707 资源指示器通过在授权服务器支持该功能时将令牌绑定到其预期受众,提供了关键的安全优势。为了启用当前和未来的采用:
- MCP 客户端必须在授权和令牌请求中包含
resource参数,如资源参数实现部分所述 - MCP 服务器必须验证呈现给它们的令牌是专门为它们签发的
安全最佳实践文档概述了为什么令牌受众验证至关重要,以及为什么明确禁止令牌传递。
5.4 令牌盗窃
获取客户端存储的令牌或在服务器上缓存或记录的令牌的攻击者可以使用看起来对资源服务器合法的请求访问受保护的资源。
客户端和服务器必须实施安全的令牌存储,并遵循 OAuth 最佳实践,如 OAuth 2.1 第 7.1 节中所述。
授权服务器应该发布短期访问令牌,以减少泄露令牌的影响。对于公共客户端,授权服务器必须按照 OAuth 2.1 第 4.3.1 节"令牌端点扩展"中的描述轮换刷新令牌。
5.5 通信安全
实现必须遵循 OAuth 2.1 第 1.5 节"通信安全"。
具体来说:
- 所有授权服务器端点必须通过 HTTPS 提供。
- 所有重定向 URI 必须是
localhost或使用 HTTPS。
5.6 授权码保护
获取授权响应中包含的授权码的攻击者可以尝试兑换授权码以获取访问令牌或以其他方式使用授权码。(在 OAuth 2.1 第 7.5 节中进一步描述)
为了缓解这种情况,MCP 客户端必须按照 OAuth 2.1 第 7.5.2 节实施 PKCE,并且必须在继续授权之前验证 PKCE 支持。PKCE 通过要求客户端创建秘密验证器-质询对来帮助防止授权码拦截和注入攻击,确保只有原始请求者可以交换授权码以获取令牌。
当技术上能够使用时,MCP 客户端必须按照 OAuth 2.1 第 4.1.1 节的要求使用 S256 代码质询方法。
由于 OAuth 2.1 和 PKCE 规范没有定义客户端发现 PKCE 支持的机制,MCP 客户端必须依赖授权服务器元数据来验证此功能:
OAuth 2.0 授权服务器元数据:如果
code_challenge_methods_supported不存在,授权服务器不支持 PKCE,MCP 客户端必须拒绝继续。OpenID Connect Discovery 1.0:虽然 OpenID Provider Metadata没有定义
code_challenge_methods_supported,但此字段通常由 OpenID 提供商包括。MCP 客户端必须验证提供程序元数据响应中code_challenge_methods_supported的存在。如果该字段不存在,MCP 客户端必须拒绝继续。
提供 OpenID Connect Discovery 1.0 的授权服务器必须在其元数据中包括 code_challenge_methods_supported 以确保 MCP 兼容性。
5.7 开放重定向
攻击者可以制作恶意重定向 URI,将用户定向到网络钓鱼网站。
MCP 客户端必须在授权服务器处注册重定向 URI。
授权服务器必须根据预注册的值验证精确的重定向 URI,以防止重定向攻击。
MCP 客户端应该在授权码流程中使用和验证状态参数,并丢弃任何不包括或与原始状态不匹配的结果。
授权服务器必须采取预防措施以防止将用户代理重定向到不受信任的 URI,遵循 OAuth 2.1 第 7.12.2 节中列出的建议
授权服务器应该仅在它信任重定向 URI 时才自动重定向用户代理。如果 URI 不受信任,授权服务器可以通知用户并依赖用户做出正确的决定。
5.8 客户端 ID 元数据文档安全
实施客户端 ID 元数据文档时,授权服务器必须考虑 OAuth 客户端 ID 元数据文档第 6 节中详细说明的安全影响。关键考虑因素包括:
授权服务器滥用保护
授权服务器从未知客户端获取 URL 作为输入并获取该 URL。恶意客户端可以使用此操作触发授权服务器对任意 URL 发出请求,例如对授权服务器有权访问的私有管理端点的请求。
获取元数据文档的授权服务器应该考虑 服务器端请求伪造 (SSRF) 风险,如 OAuth 客户端 ID 元数据文档:服务器端请求伪造 (SSRF) 攻击中所述。
Localhost 重定向 URI 风险
客户端 ID 元数据文档本身无法防止 localhost URL 伪造。攻击者可以通过以下方式声称自己是任何客户端:
- 提供合法客户端的元数据 URL 作为其
client_id - 绑定到任何
localhost端口,并提供该地址作为 redirect_uri - 在用户批准时通过重定向接收授权码
服务器将看到合法客户端的元数据文档,用户将看到合法客户端的名称,使攻击检测变得困难。
授权服务器:
- 应该为仅
localhost的重定向 URI 显示额外的警告 - 可以为增强安全性要求额外的证明机制
- 必须在授权期间清楚地显示重定向 URI 主机名
信任策略
授权服务器可以实施基于域的信任策略:
- 受保护服务器的受信任域的允许列表
- 接受任何 HTTPS
client_id(用于开放服务器) - 未知域的声誉检查
- 基于域年龄或证书验证的限制
- 显著显示 CIMD 和其他相关客户端主机名以防止网络钓鱼
服务器完全控制其访问策略。
5.9 混淆副手问题
攻击者可以利用充当第三方 API 中间人的 MCP 服务器,导致混淆副手漏洞。通过使用被盗的授权码,他们可以在未经用户同意的情况下获取访问令牌。
使用静态客户端 ID 的 MCP 代理服务器必须在转发到第三方授权服务器之前获得每个动态注册的客户端的用户同意(这可能需要额外的同意)。
5.10 访问令牌权限限制
如果服务器接受为其他资源签发的令牌,攻击者可以未经授权访问或以其他方式危及 MCP 服务器。
此漏洞有两个关键维度:
- **受众验证失败。**当 MCP 服务器不验证令牌是专门为其签发的时(例如,通过受众声明,如 RFC9068中所述),它可能接受最初为其他服务签发的令牌。这破坏了基本的 OAuth 安全边界,允许攻击者跨不同的服务重复使用合法令牌,而不是预期的服务。
- **令牌传递。**如果 MCP 服务器不仅接受具有错误受众的令牌,而且还将这些未修改的令牌转发到下游服务,它可能导致“混淆副手"问题,其中下游 API 可能会错误地信任令牌,就好像它来自 MCP 服务器一样,或者假设令牌已由上游 API 验证。有关其他详细信息,请参阅安全最佳实践指南的令牌传递部分。
MCP 服务器必须在处理请求之前验证访问令牌,确保访问令牌是专门为 MCP 服务器签发的,并采取所有必要的步骤以确保不会向未经授权的方返回数据。
MCP 服务器必须遵循 OAuth 2.1 - 第 5.2 节中的指南来验证入站令牌。
MCP 服务器必须仅接受专门为其预期的令牌,并且必须拒绝不在受众声明中包括它们的令牌,或以其他方式验证它们是令牌的预期接收者。有关详细信息,请参阅安全最佳实践令牌传递部分。
如果 MCP 服务器向上游 API 发出请求,它可以将它们作为 OAuth 客户端对待。在上游 API 使用的访问令牌是一个单独的令牌,由上游授权服务器签发。MCP 服务器不得传递它从 MCP 客户端收到的令牌。
MCP 客户端必须实施并使用 RFC 8707 - OAuth 2.0 的资源指示器中定义的 resource 参数,以明确指定请求令牌的目标资源。此要求与 RFC 9728 第 7.4 节中的建议一致。这确保访问令牌绑定到其预期资源,并且不能跨不同服务被滥用。
6. 第三方授权流程
6.1 概述
MCP 服务器可以通过第三方授权服务器支持委托授权。在此流程中,MCP 服务器同时充当 OAuth 客户端(对第三方授权服务器)和 OAuth 授权服务器(对 MCP 客户端)。
这种模式允许 MCP 服务器利用现有的身份提供商(如 Google、GitHub、Microsoft 等)进行用户身份验证和授权,而无需维护自己的用户数据库和身份验证系统。
6.2 流程描述
第三方授权流程包括以下步骤:
- 初始请求:MCP 客户端启动与 MCP 服务器的标准 OAuth 流程
- 委托重定向:MCP 服务器将用户重定向到第三方授权服务器
- 用户授权:用户在第三方服务器上进行身份验证并授权
- 回调处理:第三方服务器带着授权码重定向回 MCP 服务器
- 令牌交换:MCP 服务器用授权码交换第三方访问令牌
- 令牌生成:MCP 服务器生成绑定到第三方会话的自己的访问令牌
- 流程完成:MCP 服务器完成与 MCP 客户端的原始 OAuth 流程
sequenceDiagram
participant B as 用户代理(浏览器)
participant C as MCP 客户端
participant M as MCP 服务器
participant T as 第三方授权服务器
C->>M: 初始 OAuth 请求
M->>B: 重定向到第三方 /authorize
B->>T: 授权请求
Note over T: 用户授权
T->>B: 重定向到 MCP 服务器回调
B->>M: 授权码
M->>T: 用码交换令牌
T->>M: 第三方访问令牌
Note over M: 生成绑定的 MCP 令牌
M->>B: 重定向到 MCP 客户端回调
B->>C: MCP 授权码
C->>M: 用码交换令牌
M->>C: MCP 访问令牌
6.2.1 会话绑定要求
实现第三方授权的 MCP 服务器必须:
- 维护第三方令牌和已发行 MCP 令牌之间的安全映射
- 在认可 MCP 令牌之前验证第三方令牌状态
- 实施适当的令牌生命周期管理
- 处理第三方令牌过期和更新
6.2.2 安全考虑
实施第三方授权时,服务器必须:
- 验证所有重定向 URI
- 安全存储第三方凭据
- 实施适当的会话超时处理
- 考虑令牌链接的安全影响
- 为第三方授权失败实施适当的错误处理
7. 最佳实践
7.1 作为公共 OAuth 2.1 客户端的本地客户端
我们强烈建议本地客户端作为公共客户端实现 OAuth 2.1:
- 利用代码挑战 (PKCE) 进行授权请求,以防止拦截攻击
- 实施适合本地系统的安全令牌存储
- 遵循令牌刷新最佳实践以维护会话
- 正确处理令牌过期和更新
7.2 授权元数据发现
我们强烈建议所有客户端实施元数据发现。这减少了用户手动提供端点或客户端回退到定义的默认值的需要。
7.3 动态客户端注册
由于客户端无法提前知道 MCP 服务器集,我们强烈建议实施动态客户端注册。这允许应用程序自动向 MCP 服务器注册,并消除了用户手动获取客户端 ID 的需要。
8. MCP 授权扩展
核心协议有几个授权扩展,它们定义了额外的授权机制。这些扩展是:
- 可选的 - 实现可以选择采用这些扩展
- 附加的 - 扩展不修改或破坏核心协议功能;它们添加新功能,同时保留核心协议行为
- 可组合的 - 扩展是模块化的,设计为无冲突地协同工作,允许实现同时采用多个扩展
- 独立版本控制 - 扩展遵循核心 MCP 版本控制周期,但可能根据需要采用独立版本控制
可以在 MCP 授权扩展 存储库中找到支持的扩展列表。