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,并告知客户端在哪里可以找到授权信息,这些信息记录在 受保护资源元数据(Protected Resource Metadata,PRM)文档 中。该文档由 MCP 服务器托管,遵循可预测的路径模式,并通过 WWW-Authenticate 头中的 resource_metadata 参数提供给客户端。
这告诉客户端:MCP 服务器需要授权,以及去哪里获取启动授权流程所需的信息。
2

受保护资源元数据发现

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

授权服务器发现

接下来,客户端通过获取授权服务器的元数据来了解它能做什么。如果 PRM 文档列出了多个授权服务器,客户端可以决定使用哪一个。选定授权服务器后,客户端会构造一个标准的元数据 URI,并向 OpenID Connect (OIDC) DiscoveryOAuth 2.0 Auth Server Metadata 端点发起请求(取决于授权服务器的支持情况), 并获取另一组元数据属性,从而知道完成授权流程所需的端点。
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,以支持我们的 scopes,并允许我们的主机(本地机器)动态注册客户端,因为默认策略会限制匿名动态客户端注册。 在 Keycloak 仪表板中进入 Client scopes,并创建一个新的 mcp:tools scope。我们将使用它来访问 MCP 服务器上的所有工具。
创建 scope 后,请确保将其类型设置为 Default,并将 Include in token scope 开关打开,因为令牌验证需要这一设置。 接下来,我们还要为 Keycloak 签发的令牌设置一个 audience。配置 audience 很重要,因为它会将预期目标直接嵌入到签发的访问令牌中。这有助于你的 MCP 服务器验证它收到的令牌是否 באמת是为它本身而不是其他 API 准备的。这是帮助避免 token passthrough 场景的关键。 为此,打开你的 mcp:tools client scope,点击 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 通信,例如进行 token introspection。要执行此操作:
  1. 转到 Clients
  2. 点击 Create client
  3. 为你的客户端提供一个唯一的 Client ID,然后点击 Next
  4. 启用 Client authentication,然后点击 Next
  5. 点击 Save
值得注意的是,token introspection 只是可用于验证令牌的众多方法之一。也可以借助各语言和平台各自的独立库来完成。 打开客户端详情后,进入 Credentials,记下 Client Secret
处理密钥切勿将客户端凭证直接嵌入代码中。我们建议使用环境变量或专门的密钥存储方案。
完成 Keycloak 配置后,每次触发授权流程时,你的 MCP 服务器都会收到一个类似下面的令牌:
解码后,它会像这样:
嵌入式 Audience请注意令牌中嵌入的 aud claim——它当前被设置为测试 MCP 服务器的 URI,并且是根据我们之前配置的 scope 推断出来的。这一点在我们的实现中对于验证非常重要。

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

相关标准与文档

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