MCP 中的 OAuth:在工具服务器中传递用户身份
当你第一次将 MCP 服务器接入真实的生产系统时,你会发现教程中轻描淡写的一点:该协议赋予了智能体(Agent)能力,但并没有给工具服务器一个每个审计日志都要求的答案——这是代表哪个人执行的操作? 你可以在不解决这个问题的情况下交付一个可运行的演示 demo,但如果不解决它,你无法向受监管的企业交付产品。而这两种状态之间的鸿沟,几乎完全是一个伪装成 OAuth 问题的分布式系统问题。
团队在这个鸿沟中寻求的解决方案,大致按尝试顺序排列,就像是把 OAuth 工作组十五年来一直警告的每一种反模式都游览了一遍。在 MCP 服务器环境中共享服务账号;将长期有效的个人令牌粘贴到配置中;或是乐观地认为“我们只需转发用户的会话 Cookie,让下游服务去处理就好”。每种方案在预发环境中都有效。但在安全审查第一次真正介入时,每种方案都会以不同的方式崩盘。
到 2026 年定稿的 MCP 授权规范高度依赖 OAuth 2.1 及其 PKCE、RFC 8707 资源指示器、RFC 7591 动态客户端注册以及 RFC 9728 受保护资源元数据。这一套组合拳告诉你智能体如何为 MCP 服务器获取令牌。它刻意没有告诉你 MCP 服务器随后如何调用它执行任务所需的其他五个系统。这第二次跳转——智能体 → MCP 服务器 → 下游 API——正是身份透传(Identity Threading)存在的地方,也是大多数生产部署仍在即兴发挥的地方。
你会在实际应用中见到的三种失败模式
共享服务账号是最常见的,因为它是最简单的。MCP 服务器持有单个 GitHub PAT、单个 Jira API 密钥或单个内部服务令牌,所有的工具调用都以该身份发出。系统可以运行。但随后有人通过智能体删除了生产资源,审计日志显示操作者是 [email protected],应急响应团队无法确定是哪个自然人触发了它。更糟糕的是,下游服务通常会做出的每一个基于用户的授权决策——Alice 能看到这个 PR 吗?Bob 能删除这个 Issue 吗?——现在都需要 MCP 服务器根据一个它并不拥有的权限模型来重新实现,而且往往实现得很拙劣。这就是多了一道手续的混淆代理(Confused Deputy)问题,也是 OWASP 的 MCP Top 10 中明确指出的 MCP01:令牌管理不当和密钥泄露。
长期有效的每用户令牌是第二招。每个用户生成一个个人访问令牌(PAT),MCP 服务器根据用户 ID 进行存储,每次工具调用都使用正确的令牌。审计追踪是正确的,但你刚刚构建了一个没有任何真实凭证库所具备的轮换、撤销或范围收窄特性的凭证库。这些令牌没有你的安全团队能够接受的过期时间,授予的权限范围往往远超智能体所 需,并且存储在 MCP 服务器碰巧使用的任何存储介质中——通常是 Postgres 的一个字段,偶尔是环境变量,有时甚至是磁盘上的文件。第一次钓鱼事件会很快让其中的风险暴露无遗。
会话 Cookie 转发是第三招,也是失败得最令人意外的一招。理由听起来很合理:用户已经登录了,智能体运行在他们的浏览器上下文中,只需转发会话,让下游服务看到它一贯看到的已认证用户即可。只要智能体和所有工具服务器都位于同一个 SSO 域内,这种方案就有效。一旦工具服务器位于不同的 Cookie 作用域——不同的子域名配置、不同的 OIDC 提供商或合作伙伴 SaaS——Cookie 要么无法到达,要么在到达时被剥离了使其有效的跨站标志。出于类似原因,MCP 规范明确禁止令牌转发(Token Passthrough):为受众 A 签发的令牌永远不应呈现给受众 B,因为受众声明(Audience Claim)是防止一个服务被欺骗去冒充另一个服务的核心机制。
协议真正解决了什么,又给你留下了什么
2026 版 MCP 授权规范是一次真正的改进,准确了解它涵盖的内容非常重要。MCP 服务器被建模为 OAuth 2.1 资源服务器。MCP 客户端是一个 OAuth 2.1 客户端。PKCE 是强制性的。通过 RFC 7591 进行的动态客户端注册意味着 Claude Desktop 或 Cursor 可以在一个它们从未见过的 MCP 服务器上出现,完成注册并完成授权码流程,而无需任何带外协调。通过 RFC 8707 实现的资源指示器要求客户端指明它打算调用的特定 MCP 服务器 ,这让授权服务器能够将生成的令牌受众声明限制得足够窄,从而无法被静默重放到另一个服务器。通过 RFC 9728 实现的受保护资源元数据让 MCP 服务器可以发布其授权服务器的位置,以便客户端可以发现认证流程而非硬编码。
- https://modelcontextprotocol.io/specification/draft/basic/authorization
- https://dasroot.net/posts/2026/04/mcp-authorization-specification-oauth-2-1-resource-indicators/
- https://stackoverflow.blog/2026/01/21/is-that-allowed-authentication-and-authorization-in-model-context-protocol/
- https://datatracker.ietf.org/doc/html/rfc8693
- https://www.rfc-editor.org/rfc/rfc8707.html
- https://github.com/modelcontextprotocol/modelcontextprotocol/issues/214
- https://auth0.com/ai/docs/mcp/get-started/call-your-apis-on-users-behalf
- https://www.solo.io/blog/mcp-authorization-patterns-for-upstream-api-calls
- https://bishopfox.com/blog/otto-support-confused-deputy
- https://bishopfox.com/blog/otto-support-ssrf-token-passthrough-with-mcp
- https://owasp.org/www-project-mcp-top-10/2025/MCP01-2025-Token-Mismanagement-and-Secret-Exposure
- https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow
- https://www.scalekit.com/blog/enterprise-mcp-how-identity-sso-and-scoped-auth-actually-work
- https://www.aembit.io/blog/mcp-oauth-2-1-pkce-and-the-future-of-ai-authorization/
- https://www.osohq.com/learn/authorization-for-ai-agents-mcp-oauth-21
