当智能体代表已登录用户调用 MCP 工具时,下游 API 的授权问题就绕不开。最省事的做法是把用户的访问令牌交给 MCP 服务器,让它原样转发这个 Bearer 令牌。这种模式搭起来容易,出错也容易。

打开网易新闻 查看精彩图片

典型流程是这样的:用户先登录你的应用(Entra ID、Auth0、Cognito 等);智能体或宿主把 Authorization: Bearer 附加到 MCP 会话或每次工具调用上;MCP 服务器再把该请求头复制给 Salesforce、GitHub、内部 REST API 或数据库网关。看起来没问题,因为下游调用仍然"以用户身份"进行。问题都藏在细节里。

转发令牌的几处硬伤

  • 令牌权限过宽:用户访问令牌往往携带大量作用域。你的 MCP 工具只需要 orders.read,令牌却同时允许 users.admin。谁从 MCP 进程里偷走这个令牌,就拿到了整套权限。
  • 受众不匹配:访问令牌是为特定受众(你的 API)签发的。转发给另一个 API,要么校验失败,要么更糟——某些技术栈会宽松接受。为 MCP 宿主签发的令牌,不该被所有后端接受。
  • 泄漏进日志和模型上下文:框架会记录请求头,智能体会把工具参数和错误塞进提示词。一段堆栈跟踪或啰嗦的 isError 载荷,就能把 Bearer 令牌打印进操作者、其他工具或模型都能看到的记录里。
  • 重放:从内存、磁盘缓存或 CI 日志里偷走的长效令牌,在过期前都能被重放。持久化"会话"状态的 MCP 服务器会让情况更糟。
  • 混淆代理:MCP 服务器成了一个可以被诱导的代理,用用户的令牌执行用户并不打算执行的操作——尤其当工具参数来自不可信内容时。

默认设计:不转发原始用户令牌

宿主负责认证用户,并用服务端会话 ID 建立 MCP 会话,而不是在每次工具载荷里塞原始访问令牌。当工具需要访问下游 API 时,由 MCP 服务器(或边车)执行令牌交换/代表用户(OBO)流程,签发一个新的、短时效的、绑定正确受众、且只含该 API 最小作用域的新令牌。工具结果只返回数据,绝不返回凭据;任何错误文本在到达模型之前,都要剥掉 Authorization 值。

在微软 Entra ID 上这就是 OBO 流程。其他身份提供商可以找令牌交换(RFC 8693),或者采用后端换前端模式,由它持有刷新令牌并签发针对具体 API 的凭据。

原型阶段必须传令牌时

  • 限定单一受众和极短有效期(以分钟计)。
  • 只走 TLS,绝不作为模型生成的工具参数传递。
  • 只存内存,不写入 MCP 资源体或日志。
  • 激进轮换与吊销,上线前关掉这种模式。

实施清单

  • 下游令牌按 API 受众签发,不复用聊天应用受众的令牌。
  • 作用域取工具所需最小值,读写工具分开。
  • MCP 工具 schema 里绝不出现让模型填写的 access_token 参数。
  • 日志与追踪对 Authorization 及已知令牌模式做脱敏。
  • 重放防御:短过期、在可用时采用发送方约束令牌(DPoP/mTLS)。
  • 高影响工具即使令牌已限定作用域,也要人工批准。

MCP 服务器应当能证明用户是谁,而不是变成一个自由漂浮的主密钥持有者。换取你需要的最窄令牌,绑定到正确的受众,把 Bearer 字符串挡在提示词和日志之外。