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 指南。
你将能够通过浏览器访问 http://localhost:8080 上的 Keycloak 授权服务器。
在默认配置下运行时,Keycloak 已经支持我们为 MCP 服务器所需的许多能力,包括动态客户端注册。你可以通过查看 OIDC 配置来确认这一点,该配置可在以下地址获取:
我们还需要设置 Keycloak 以支持我们的作用域,并允许我们的主机(本地机器)动态注册客户端,因为默认策略会限制匿名动态客户端注册。 前往 Keycloak 仪表板中的 客户端作用域,并创建一个新的 mcp:tools 作用域。我们将使用它来访问 MCP 服务器上的所有工具。
创建作用域后,请确保将其类型设置为 默认,并将 包含在令牌作用域中 开关打开,因为令牌验证会需要它。 现在我们还要为 Keycloak 签发的令牌设置一个 受众。配置受众很重要,因为它会将预期目标直接嵌入到签发的访问令牌中。这有助于你的 MCP 服务器验证它收到的令牌是否确实是为它准备的,而不是为其他 API 准备的。这对于避免令牌透传场景至关重要。 为此,打开你的 mcp:tools 客户端作用域,点击 映射器,然后点击 配置新映射器。选择 受众
对于 名称,使用 audience-config。为 包含的自定义受众 添加一个值,设置为 http://localhost:3000。这将是我们测试服务器的 URI。
不用于生产环境上面的受众配置用于测试。对于生产场景,还需要额外的设置和配置,以确保已签发令牌的受众被正确约束。具体来说,受众需要基于从客户端传入的 resource 参数,而不是固定值。
现在,导航到 客户端,然后是 客户端注册,再到 受信任的主机。禁用 客户端 URI 必须匹配 设置,并添加你正在测试的主机。你可以通过在 Linux 或 macOS 上运行 ifconfig 命令,或在 Windows 上运行 ipconfig 来获取当前主机 IP。你可以通过查看 Keycloak 日志中类似 Failed to verify remote host : 192.168.215.1 的行来找到你需要添加的 IP 地址。请检查该 IP 地址是否与你的主机关联。这可能取决于你的 Docker 设置而对应一个桥接网络。
获取主机如果你是从容器中运行 Keycloak,你也可以在容器日志的终端中看到主机 IP。
最后,我们需要注册一个新的客户端,供 MCP 服务器本身 使用,以便与 Keycloak 进行诸如 令牌自省 之类的交互。为此:
  1. 前往 客户端
  2. 点击 创建客户端
  3. 为你的客户端提供一个唯一的 客户端 ID,然后点击 下一步
  4. 启用 客户端身份验证,然后点击 下一步
  5. 点击 保存
值得注意的是,令牌自省只是验证令牌的可用方法之一。也可以借助针对每种语言和平台的独立库来完成。 打开客户端详情后,前往 凭据 并记下 客户端密钥
处理密钥切勿将客户端凭据直接嵌入到你的代码中。我们建议使用环境变量或专门的密钥存储方案。
配置好 Keycloak 后,每次触发授权流程时,你的 MCP 服务器都会收到类似这样的令牌:
解码后,它会像这样:
嵌入式受众注意令牌中嵌入的 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 授权建立在以下这些成熟的标准之上: 更多详细信息,请参阅: 理解这些标准将帮助你正确实现授权,并在问题出现时进行排查。