Skip to main content
模型上下文协议(MCP)中的授权用于保护 MCP 服务器暴露的敏感资源和操作的访问。如果你的 MCP 服务器处理用户数据或管理操作,授权可确保只有被允许的用户才能访问其端点。 MCP 使用标准化的授权流程来在 MCP 客户端和 MCP 服务器之间建立信任。其设计并不专注于某一种特定的授权或身份系统,而是遵循 OAuth 2.1 中概述的约定。有关详细信息,请参阅 授权规范

何时应该使用授权?

虽然 MCP 服务器的授权是可选的,但在以下情况下强烈建议使用:
  • 你的服务器访问用户特定数据(电子邮件、文档、数据库)
  • 你需要审计是谁执行了哪些操作
  • 你的服务器授予对其 API 的访问权限,而这些 API 需要用户同意
  • 你正在为具有严格访问控制的企业环境构建
  • 你希望按用户实施速率限制或使用情况跟踪
本地 MCP 服务器的授权对于使用 STDIO 传输 的 MCP 服务器,你可以改用基于环境的凭据,或者直接嵌入到 MCP 服务器中的第三方库提供的凭据。由于基于 STDIO 构建的 MCP 服务器是本地运行的,因此在获取用户凭据方面,它拥有一系列灵活的选择,这些选择可能会也可能不会依赖于浏览器内的身份验证和授权流程。而 OAuth 流程则是为基于 HTTP 的传输设计的,在这种情况下,MCP 服务器是远程托管的,客户端使用 OAuth 来建立用户已被授权访问该远程服务器的状态。

授权流程:逐步说明

让我们来看看当客户端想要连接到你的受保护 MCP 服务器时,会发生什么:
1

初始握手

当你的 MCP 客户端第一次尝试连接时,服务器会返回 401 Unauthorized,并告诉客户端在哪里查找授权信息,这些信息记录在 受保护资源元数据(PRM)文档 中。该文档由 MCP 服务器托管,遵循可预测的路径模式,并通过 WWW-Authenticate 头中的 resource_metadata 参数提供给客户端。
这告诉客户端,该 MCP 服务器需要授权,以及从哪里获取启动授权流程所需的信息。
2

受保护资源元数据发现

借助指向 PRM 文档的 URI,客户端将获取这些元数据,以了解授权服务器、支持的作用域以及其他资源信息。数据通常封装在一个 JSON 数据块中,类似如下内容。
你可以在 RFC 9728 第 3.2 节 中看到一个更完整的示例。
3

授权服务器发现

接下来,客户端通过获取授权服务器的元数据来发现其能力。如果 PRM 文档列出了多个授权服务器,客户端可以决定使用哪一个。在选定授权服务器后,客户端将构造标准元数据 URI,并向 OpenID Connect(OIDC)发现OAuth 2.0 授权服务器元数据 端点发起请求(取决于授权服务器支持情况), 并获取另一组元数据属性,从而知道完成授权流程所需的端点。
4

客户端注册

在完成所有元数据获取之后,客户端现在需要确保自己已在授权服务器上注册。这可以通过两种方式完成。首先,客户端可以被特定授权服务器预先注册,在这种情况下,它可以拥有嵌入式的客户端注册信息,用于完成授权流程。或者,客户端可以使用动态客户端注册(DCR)来动态地向授权服务器注册自身。后一种场景要求授权服务器支持 DCR。如果授权服务器确实支持 DCR,客户端将向 registration_endpoint 发送包含其信息的请求:
如果注册成功,授权服务器将返回一个包含客户端注册信息的 JSON 数据块。
无 DCR 或预先注册如果某个 MCP 客户端连接到一个 MCP 服务器,而该服务器使用的授权服务器不支持 DCR,同时该客户端也没有在该授权服务器上预先注册,那么就需要客户端开发者为最终用户提供一种手段,让其手动输入客户端信息。
5

用户授权

客户端现在需要打开浏览器访问 /authorize 端点,用户可以在这里登录并授予所需权限。随后,授权服务器会重定向回客户端,并返回一个授权码,客户端再用该授权码交换令牌:
访问令牌是客户端用来对 MCP 服务器请求进行身份验证的凭证。此步骤遵循标准的 OAuth 2.1 带 PKCE 的授权码 约定。
6

发起已认证请求

最后,客户端可以使用嵌入在 Authorization 头中的访问令牌向你的 MCP 服务器发起请求:
如果令牌有效且拥有所需权限,MCP 服务器就需要验证该令牌并处理请求。

实现示例

为了开始进行实际实现,我们将使用一个托管在 Docker 容器中的 Keycloak 授权服务器。Keycloak 是一个开源授权服务器,可以很容易地在本地部署,用于测试和实验。 请确保你已下载并安装 Docker Desktop。我们将需要它来在我们的开发机器上部署 Keycloak。

Keycloak 设置

从你的终端应用中运行以下命令来启动 Keycloak 容器:
该命令会将 Keycloak 容器镜像拉取到本地,并初始化基本配置。它将运行在 8080 端口,并使用 admin 用户和 admin 密码。
不适用于生产环境上面的配置可能适合测试和实验;但是,你绝不应在生产环境中使用它。有关如何为需要可靠性、安全性和高可用性的场景部署授权服务器的更多细节,请参阅 为生产环境配置 Keycloak 指南。
你将能够通过浏览器访问 Keycloak 授权服务器,地址为 http://localhost:8080
在默认配置下运行时,Keycloak 已经支持我们为 MCP 服务器所需的许多能力,包括动态客户端注册。你可以通过查看 OIDC 配置来确认这一点,地址如下:
我们还需要配置 Keycloak 以支持我们的作用域,并允许我们的主机(本地机器)动态注册客户端,因为默认策略会限制匿名动态客户端注册。 前往 Keycloak 仪表板中的 Client scopes,创建一个新的 mcp:tools 作用域。我们将使用它来访问 MCP 服务器上的所有工具。
创建作用域后,确保将其类型设置为 Default,并打开 Include in token scope 开关,因为这在令牌验证中是必需的。 现在我们还需要为 Keycloak 签发的令牌设置一个 audience。配置 audience 很重要,因为它会将预期目标直接嵌入到签发的访问令牌中。这有助于你的 MCP 服务器验证它收到的令牌确实是发给它本身的,而不是其他 API。这对于避免令牌透传场景至关重要。 为此,打开你的 mcp:tools 客户端作用域,点击 Mappers,然后点击 Configure a new mapper。选择 Audience
Name 中使用 audience-config。为 Included Custom Audience 添加一个值,设为 http://localhost:3000。这将是我们测试服务器的 URI。
不适用于生产环境上面的 audience 配置仅用于测试。对于生产场景,还需要额外的设置和配置,以确保签发的令牌的 audience 被正确约束。具体来说,audience 需要基于从客户端传入的 resource 参数,而不是一个固定值。
现在,导航到 Clients,然后是 Client registration,再到 Trusted Hosts。禁用 Client URIs Must Match 设置,并添加你正在测试的主机。你可以在 Linux 或 macOS 上运行 ifconfig 命令,或在 Windows 上运行 ipconfig 来获取当前主机 IP。你可以通过查看 keycloak 日志中类似 Failed to verify remote host : 192.168.215.1 的一行来找到需要添加的 IP 地址。检查该 IP 地址是否与你的主机关联。这可能是你的 docker 设置中的桥接网络地址。
获取主机地址如果你是从容器中运行 Keycloak,你也可以在容器日志的终端中看到主机 IP。
最后,我们需要注册一个新的客户端,用于 MCP 服务器本身 与 Keycloak 通信,例如进行 令牌自省。要这样做:
  1. 前往 Clients
  2. 点击 Create client
  3. 为你的客户端提供一个唯一的 Client ID,然后点击 Next
  4. 启用 Client authentication,然后点击 Next
  5. 点击 Save
值得注意的是,令牌自省只是验证令牌的可用方法之一。也可以借助各语言和平台各自的独立库来完成。 打开客户端详情后,前往 Credentials 并记下 Client Secret
处理密钥永远不要将客户端凭据直接嵌入到代码中。我们建议使用环境变量或专门的密钥存储方案。
配置好 Keycloak 后,每次触发授权流程时,你的 MCP 服务器都会收到如下所示的令牌:
解码后,它会像这样:
嵌入的 Audience请注意令牌中嵌入的 aud 声明——它当前被设置为测试 MCP 服务器的 URI,并且是从我们之前配置的作用域中推导出来的。这在我们的实现中将用于验证,这一点很重要。

MCP 服务器设置

现在我们将设置我们的 MCP 服务器,以使用本地运行的 Keycloak 授权服务器。根据你偏好的编程语言,可以使用受支持的 MCP SDK 之一。 为了测试方便,我们将创建一个非常简单的 MCP 服务器,暴露两个工具——一个用于加法,另一个用于乘法。访问这些工具需要授权。
你可以在 示例仓库 中查看完整的 TypeScript 项目。在运行下面的代码之前,请确保你有一个包含以下内容的 .env 文件:
OAUTH_CLIENT_IDOAUTH_CLIENT_SECRET 与我们之前创建的 MCP 服务器客户端相关联。除了实现 MCP 授权规范之外,下面的服务器还会通过 Keycloak 执行令牌自省,以确保它从客户端接收到的令牌是有效的。它还实现了基础日志记录,方便你轻松诊断任何问题。
运行服务器后,你可以通过提供 MCP 服务器端点将它添加到你的 MCP 客户端中,例如 Visual Studio Code。有关在 TypeScript 中实现 MCP 服务器的更多详细信息,请参阅 TypeScript SDK 文档

测试 MCP 服务器

为了进行测试,我们将使用 Visual Studio Code,但任何支持 MCP 和新授权规范的客户端都可以。 按下 Cmd + Shift + P,然后选择 MCP: Add server…。选择 HTTP 并输入 http://localhost:3000。为服务器指定一个唯一名称,以便在 Visual Studio Code 中使用。在 mcp.json 中,你现在应该会看到类似下面的条目:
连接后,你会被带到浏览器,在那里系统会提示你同意 Visual Studio Code 访问 mcp:tools 作用域。
同意之后,你会在 mcp.json 中看到工具列表显示在服务器条目上方。
你可以在聊天视图中借助 # 符号来调用单个工具。

常见陷阱及其避免方法

有关全面的安全指南,包括攻击向量、缓解策略和实现最佳实践,请务必阅读 安全最佳实践。下面列出了一些关键问题。
  • 不要自行实现令牌验证或授权逻辑。对于令牌验证或授权决策等功能,请使用现成的、经过充分测试且安全的库。从零开始实现所有内容,除非你是安全专家,否则更容易出错。
  • 使用短期有效的访问令牌。根据所使用的授权服务器,这项设置可能是可配置的。我们建议不要使用长期有效的令牌——如果恶意行为者窃取了它们,他们将能够在更长时间内维持访问权限。
  • 始终验证令牌。你的服务器收到令牌并不意味着该令牌有效,或者它是为你的服务器准备的。始终验证 MCP 服务器从客户端获得的内容是否符合所需约束。
  • 将令牌存储在安全、加密的存储中。在某些场景下,你可能需要在服务器端缓存令牌。如果是这样,请确保该存储具有正确的访问控制,并且不会轻易被有权访问你服务器的恶意方窃取。你还应实施健壮的缓存回收策略,以确保你的 MCP 服务器不会重复使用已过期或其他无效的令牌。
  • 在生产环境中强制使用 HTTPS。除开发期间的 localhost 外,不要通过普通 HTTP 接受令牌或重定向回调。
  • 最小权限范围。不要使用包罗万象的范围。尽可能按工具或能力拆分访问,并在资源服务器上按路由/工具验证所需范围。
  • 不要记录凭证。绝不要记录 Authorization 头、令牌、代码或密钥。清理查询字符串和请求头。在结构化日志中对敏感字段进行脱敏。
  • 分离应用与资源服务器凭证。不要将 MCP 服务器的客户端密钥用于终端用户流程。将所有密钥存放在合适的密钥管理器中,不要放在源代码控制中。
  • 返回正确的质询信息。在 401 响应中,包含带有 Bearerrealmresource_metadataWWW-Authenticate,以便客户端发现如何进行身份验证。
  • DCR(动态客户端注册)控制。如果启用,请注意你组织特有的约束,例如受信任的主机、必需的审查以及受审计的注册。未经身份验证的 DCR 意味着任何人都可以向你的授权服务器注册任何客户端。
  • 多租户/领域混淆。除非明确支持多租户,否则应固定到单一发行方/租户。即使令牌由同一授权服务器签名,也要拒绝来自其他领域的令牌。
  • 受众/资源标识符误用。不要配置或接受通用受众(如 api)或不相关的资源。要求受众/资源必须与你配置的服务器匹配。
  • 错误详情泄露。向客户端返回通用消息,但在内部使用关联 ID 记录详细原因,以便排查问题,同时不暴露内部信息。
  • 会话标识符加固。将 Mcp-Session-Id 视为不可信输入;不要将授权绑定到它。身份验证变更时重新生成,并在服务器端验证其生命周期。

相关标准和文档

MCP 授权建立在以下这些成熟标准之上: 更多详情请参阅: 理解这些标准将帮助你正确实现授权,并在问题出现时进行排查。