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

何时应该使用授权?

虽然 MCP 服务器的授权是可选的,但在以下情况下强烈建议使用:
  • 你的服务器访问用户特定数据(电子邮件、文档、数据库)
  • 你需要审计是谁执行了哪些操作
  • 你的服务器向需要用户同意的 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) DiscoveryOAuth 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、密码为 adminadmin 用户。
不适用于生产环境上面的配置可能适合测试和实验;但是,你绝不应在生产环境中使用它。有关如何为需要可靠性、安全性和高可用性的场景部署授权服务器的更多详细信息,请参阅 为生产环境配置 Keycloak 指南。
你将能够通过浏览器访问位于 http://localhost:8080 的 Keycloak 授权服务器。
在默认配置下运行时,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,你也可以在容器日志的 Terminal 中看到主机 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 Server 设置

我们现在将设置我们的 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 意味着任何人都可以在你的授权服务器上注册任意客户端。
  • 多租户/realm 混淆。除非明确支持多租户,否则应固定到单一发行方/租户。即使令牌由同一授权服务器签名,也要拒绝来自其他 realm 的令牌。
  • 受众/资源标识符误用。不要配置或接受通用受众(如 api)或无关资源。要求受众/资源与配置的服务器匹配。
  • 错误细节泄露。向客户端返回通用消息,但在内部使用关联 ID 记录详细原因,以帮助排查问题而不暴露内部实现。
  • 会话标识符加固。将 Mcp-Session-Id 视为不受信任的输入;切勿将授权与其绑定。在身份验证变更时重新生成,并在服务器端验证生命周期。

相关标准和文档

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