Hotdry.

Article

远程 MCP Server 接入 OAuth 2.1:RFC 9728 发现、PKCE 与 resource 参数落地

依据 MCP 2025-06-18 Authorization 规范,说明 Agent 客户端如何把受保护 MCP 端点映射为 OAuth 资源服务器,并给出 Protected Resource Metadata、动态注册、PKCE 与 RFC 8707 resource 参数的可运维实现要点。

2026-06-23security

把 MCP Server 从本机 stdio 迁到可被编排器共享的 HTTPS 端点后,传输层会话(Mcp-Session-Id、SSE 续传)解决的是「如何可靠收发 JSON-RPC」;鉴权解决的是「谁有权调用 tools/call」。MCP 2025-06-18 在 HTTP 传输上把受保护服务器定义为 OAuth 2.1 资源服务器(Resource Server),客户端为 OAuth Client,并强制通过 RFC 9728 做授权服务器发现。若 Agent 网关仍用长期 API Key 拼在查询串、或把用户 OAuth 令牌原样转给下游 SaaS,会与规范中的 audience 绑定、禁止 token passthrough 等要求直接冲突。本文只讨论 MCP 官方 Authorization 章节与相关 RFC 的落地参数,不涉及某一商业 IDE 的内部凭据存储实现。

问题背景:无鉴权 HTTP MCP 与多租户 Agent 的缺口

典型生产场景里,远程 MCP 具备以下特征:

  • 多 Agent、多租户:同一 MCP 路径可能被不同编排 Pod、不同终端用户复用,需要按主体授权而非仅靠网络 ACL。
  • 工具副作用tools/call 可能触发写操作;传输层 401 与业务层 403(scope 不足)必须可区分,便于 Agent 重试或升级授权。
  • 动态服务器目录:用户配置的 MCP URL 在运行时才出现,客户端无法预先在控制台为每个 AS 手工注册 OAuth 应用。
  • 与 Streamable HTTP 并存:规范要求每一次 HTTP 请求(含同一逻辑会话内的 POST/GET/DELETE)都携带 Authorization: Bearer,不能只在 initialize 时带一次令牌。

MCP 规范明确:stdio 传输不应走本章 OAuth 流程,而应从环境变量等受控渠道取凭据;HTTP 类传输在支持鉴权时应符合本章。因此「网关只反代、不实现 401 → 元数据发现」会在客户端首次无 token 访问时无法自动拉起授权。

可落地实现:发现、注册、授权与请求头

1. 401 与 Protected Resource Metadata(RFC 9728)

客户端向 MCP 端点发起无 token 请求时,资源服务器应返回 HTTP 401,并在 WWW-Authenticate 中给出资源元数据 URL(RFC 9728 §5.1)。客户端必须能解析该头并继续发现流程。

资源元数据文档(路径由部署约定,规范示例为 /.well-known/oauth-protected-resource必须包含 authorization_servers 数组,且至少一个授权服务器。多 AS 时,由客户端按 RFC 9728 §7.6 选择使用哪一个。

最小元数据形状(字段名以 RFC 9728 为准,值需与真实部署一致):

{
  "resource": "https://mcp.example.com/mcp",
  "authorization_servers": ["https://auth.example.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["mcp:tools", "mcp:resources"]
}

运维对照项:

检查项 期望
无 token 的 POST /mcp 401 + WWW-Authenticateresource_metadata 或等效指引
resource 字段 与后续 RFC 8707 resource 参数使用的 canonical URI 一致
HTTPS 授权服务器所有端点必须为 HTTPS(OAuth 2.1 §1.5)

2. Authorization Server Metadata(RFC 8414)

客户端对选定的 AS 请求 /.well-known/oauth-authorization-server,获取 authorization_endpointtoken_endpointregistration_endpoint(若支持动态注册)等。MCP 规范要求客户端必须使用该元数据,不得硬编码猜测路径。

3. 动态客户端注册(RFC 7591,SHOULD)

规范 SHOULD 支持 RFC 7591,以便客户端在未知 MCP/AS 组合时自动拿到 client_id(及机密客户端的凭据)。不支持 DCR 的 AS 需另提供注册途径:客户端只能硬编码该 AS 的 client 配置,或向用户展示配置 UI。

Agent 平台侧建议为每次 DCR 记录:client_idas_issuerregistered_at,并与租户绑定;轮换 AS 密钥时需能重新注册或更新元数据缓存 TTL(例如 24h,失败时强制刷新)。

4. 授权码流 + PKCE(必须)

MCP 客户端必须实现 PKCE(OAuth 2.1 §7.5.2)。推荐参数(与 OAuth 2.1 常见实践一致):

参数 建议值
code_challenge_method S256
code_verifier 43–128 字符高熵随机串
state 客户端随机值,回调时严格比对,不匹配则丢弃
redirect_uri 预注册精确匹配;原生 / 桌面客户端常用 http://127.0.0.1:<port>/callback 或自定义 scheme

授权请求与令牌请求中,客户端必须携带 RFC 8707 resource 参数,标识令牌所针对的 MCP 资源(与 Protected Resource Metadata 中的 canonical URI 对齐)。规范要求:无论 AS 是否声明支持,客户端都必须发送

示例(授权端点查询片段,需 URL 编码):

response_type=code
&client_id=<client_id>
&redirect_uri=https%3A%2F%2Fagent.example.com%2Foauth%2Fcallback
&scope=mcp%3Atools
&state=<random>
&code_challenge=<S256_challenge>
&code_challenge_method=S256
&resource=https%3A%2F%2Fmcp.example.com%2Fmcp

Canonical URI 规则(摘要):须含 scheme;不得含 fragment;宜使用小写 scheme/host;路径需能唯一标识 MCP 服务(例如 https://mcp.example.com/mcp)。无效示例:mcp.example.com(缺 scheme)、带 #fragment 的 URL。

5. 访问令牌在 MCP HTTP 上的使用

规范要求(OAuth 2.1 §5):

  1. 每个发往 MCP 的 HTTP 请求都带 Authorization: Bearer <access_token>,包括同一 Mcp-Session-Id 会话内的后续 POST/GET。
  2. 禁止把 token 放在 query string。
  3. 资源服务器必须按 OAuth 2.1 §5.2 校验令牌,并验证令牌是为本 MCP 资源签发(RFC 8707 audience);失败返回 401。
  4. MCP 服务器不得接受或转发「发给其他受众」的令牌;若 MCP 再调用上游 API,必须使用上游 AS 签发的独立令牌,禁止 token passthrough

与 Streamable HTTP 组合的请求示例:

POST /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18
Mcp-Session-Id: 7f3c9e2a-4b1d-4c8a-9f0e-1a2b3c4d5e6f
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}

6. 错误码与 Agent 行为

HTTP 状态 含义 Agent 侧建议
401 缺 token、过期或 audience 错误 触发刷新令牌或重新走授权;勿用错误 token 重试写工具
403 scope 或策略拒绝 提示用户扩大授权或换工具;勿无限重试
400 畸形 OAuth/MCP 请求 记录请求 id,修配置后重试

授权服务器签发短生命周期 access token;对 public client,AS 必须按 OAuth 2.1 §4.3.1 轮换 refresh token。Agent 运行时应把 token 存于 OS 密钥链或加密的租户级密钥库,避免写入模型上下文或工具入参日志。

7. 网关与反向代理注意点

若 Nginx/Envoy 在 MCP 上游前终结 TLS 并做 WAF:

  • 不要把 Bearer token 记入 access log 默认字段;需要审计时使用哈希或截断。
  • 401 响应体与 WWW-Authenticate 头应原样透传,避免「统一 403 页面」破坏发现流程。
  • 多 AS 场景下,缓存 Protected Resource Metadata 时宜以完整 MCP base URL 为键,防止路径级 MCP 实例串元数据。

风险与边界

规范可选 vs 生产强制。 Authorization 对 MCP 实现是 OPTIONAL,但企业内网「默认无鉴权」的 HTTP MCP 一旦暴露到可被用户配置的 Agent,风险与公网等价。传输层 Origin 校验不能替代 OAuth。

resource URI 不一致。 授权时用 https://mcp.example.com,校验 audience 时期望 https://mcp.example.com/mcp(或相反)会导致合法用户持续 401。应在部署清单中固定 canonical URI,并在 CI 中用契约测试打 401→元数据→mock token 校验。

DCR 滥用。 自动注册降低摩擦,也扩大攻击面:AS 应配合速率限制、批准策略或 mTLS;Agent 厂商需防止恶意租户批量注册傀儡 client。

刷新与长会话 SSE。 Streamable HTTP 下 SSE 可能持续数小时;access token 过期时,需在不中断用户预期的前提下刷新并重连,同时遵守「每请求带 Authorization」。实现上宜在 token 剩余寿命低于阈值(例如 120s)时主动刷新,而不是在 401 后才刷新,以减少写工具半途中断。

Confused deputy 与静态 client_id 代理。 规范要求:使用静态 client_id 的 MCP 代理在把请求转发到第三方 AS 之前,必须对每个动态注册的客户端取得用户同意。自建「MCP 聚合网关」时需单独设计同意 UI 与审计,不能假设 OAuth 层已隐含授权。

stdio 与 HTTP 凭据混用。 同一工具实现若同时提供 stdio 与 HTTP,凭据来源不同;文档与配置必须隔离,避免把 Bearer token 写入子进程环境变量供无关进程读取。

参考来源

security

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com