何时应使用授权?
虽然 MCP 服务器的授权是可选的,但在以下情况中强烈建议使用:- 你的服务器访问用户特定数据(电子邮件、文档、数据库)
- 你需要审计是谁执行了哪些操作
- 你的服务器授予对其 API 的访问权限,而这些 API 需要用户同意
- 你正在为具有严格访问控制的企业环境构建服务
- 你希望为每个用户实施速率限制或使用情况跟踪
授权流程:逐步说明
让我们一步步了解,当客户端想要连接到你的受保护 MCP 服务器时会发生什么:1
初始握手
当你的 MCP 客户端首次尝试连接时,服务器会返回一个 这告诉客户端,该 MCP 服务器需要授权,以及从哪里获取启动授权流程所需的信息。
401 Unauthorized,并告诉客户端在哪里可以找到授权信息,这些信息记录在一份 受保护资源元数据(PRM)文档 中。该文档由 MCP 服务器托管,遵循可预测的路径模式,并通过 WWW-Authenticate 头中的 resource_metadata 参数提供给客户端。2
受保护资源元数据发现
有了指向 PRM 文档的 URI,客户端将获取这些元数据,以了解授权服务器、支持的范围以及其他资源信息。数据通常封装在一个 JSON 数据块中,如下所示。你可以在 RFC 9728 第 3.2 节 中看到更完整的示例。
3
授权服务器发现
接下来,客户端通过获取授权服务器的元数据来了解它能做什么。如果 PRM 文档列出了多个授权服务器,客户端可以决定使用哪一个。选定授权服务器后,客户端会构造一个标准的元数据 URI,并向 OpenID Connect (OIDC) 发现 或 OAuth 2.0 授权服务器元数据 端点发起请求(取决于授权服务器支持情况),
并获取另一组元数据属性,从而知道完成授权流程所需的端点。
4
客户端注册
在所有元数据都准备好之后,客户端现在需要确保自己已在授权服务器上完成注册。这可以通过两种方式完成。首先,客户端可以在某个授权服务器上预注册,在这种情况下,它可以使用内嵌的客户端注册信息来完成授权流程。另外,客户端也可以使用动态客户端注册(DCR)动态地向授权服务器注册自身。后一种情况要求授权服务器支持 DCR。如果授权服务器支持 DCR,客户端将向 如果注册成功,授权服务器将返回一个包含客户端注册信息的 JSON 数据块。
registration_endpoint 发送包含其信息的请求:5
用户授权
客户端现在需要打开浏览器访问 访问令牌是客户端用来对 MCP 服务器请求进行身份验证的凭证。此步骤遵循标准的 OAuth 2.1 带 PKCE 的授权码流程 约定。
/authorize 端点,用户可以在此登录并授予所需权限。随后,授权服务器会重定向回客户端,并返回一个授权码,客户端再用该授权码去交换令牌:6
发起已认证请求
最后,客户端可以使用嵌入在 MCP 服务器需要验证该令牌,并在令牌有效且具备所需权限时处理请求。
Authorization 头中的访问令牌向你的 MCP 服务器发起请求:实现示例
为了开始实际实现,我们将使用一个托管在 Docker 容器中的 Keycloak 授权服务器。Keycloak 是一个开源授权服务器,可以轻松在本地部署,用于测试和实验。 请确保你已下载并安装 Docker Desktop。我们需要它来在开发机器上部署 Keycloak。Keycloak 设置
从你的终端应用中,运行以下命令来启动 Keycloak 容器:8080 端口,并使用 admin 用户名和 admin 密码。
你将能够通过浏览器访问 http://localhost:8080 上的 Keycloak 授权服务器。
mcp:tools 作用域。我们将使用它来访问 MCP 服务器上的所有工具。
mcp:tools 客户端作用域,点击 映射器,然后点击 配置新映射器。选择 受众。
audience-config。为 包含的自定义受众 添加一个值,设置为 http://localhost:3000。这将是我们测试服务器的 URI。
现在,导航到 客户端,然后是 客户端注册,再到 受信任的主机。禁用 客户端 URI 必须匹配 设置,并添加你正在测试的主机。你可以通过在 Linux 或 macOS 上运行 ifconfig 命令,或在 Windows 上运行 ipconfig 来获取当前主机 IP。你可以通过查看 Keycloak 日志中类似 Failed to verify remote host : 192.168.215.1 的行来找到你需要添加的 IP 地址。请检查该 IP 地址是否与你的主机关联。这可能取决于你的 Docker 设置而对应一个桥接网络。
- 前往 客户端。
- 点击 创建客户端。
- 为你的客户端提供一个唯一的 客户端 ID,然后点击 下一步。
- 启用 客户端身份验证,然后点击 下一步。
- 点击 保存。
MCP 服务器设置
我们现在将设置我们的 MCP 服务器,使其使用本地运行的 Keycloak 授权服务器。根据你偏好的编程语言,可以使用受支持的 MCP SDK 之一。 为了测试目的,我们将创建一个极其简单的 MCP 服务器,暴露两个工具——一个用于加法,另一个用于乘法。访问这些工具将需要授权。- TypeScript
- Python
- C#
你可以在 示例仓库 中查看完整的 TypeScript 项目。在运行下面的代码之前,请确保你有一个包含以下内容的 运行服务器后,你可以通过提供 MCP 服务器端点将其添加到你的 MCP 客户端中,例如 Visual Studio Code。有关使用 TypeScript 实现 MCP 服务器的更多详细信息,请参阅 TypeScript SDK 文档。
.env 文件:OAUTH_CLIENT_ID 和 OAUTH_CLIENT_SECRET 与我们之前创建的 MCP 服务器客户端相关联。除了实现 MCP 授权规范之外,下面的服务器还通过 Keycloak 执行令牌内省,以确保它从客户端接收到的令牌有效。它还实现了基本日志记录,方便你轻松诊断任何问题。测试 MCP 服务器
为了进行测试,我们将使用 Visual Studio Code,但任何支持 MCP 和新授权规范的客户端都可以。 按下 Cmd + Shift + P,然后选择 MCP: Add server…。选择 HTTP 并输入http://localhost:3000。为该服务器指定一个唯一的名称,以便在 Visual Studio Code 中使用。在 mcp.json 中,你现在应该会看到类似下面的条目:
mcp:tools 作用域。
mcp.json 中服务器条目的上方看到列出的工具。
# 符号调用单个工具。
常见陷阱及其避免方法
有关全面的安全指南,包括攻击向量、缓解策略和实施最佳实践,请务必阅读 安全最佳实践。下面列出了一些关键问题。- 不要自行实现令牌验证或授权逻辑。请使用现成的、经过充分测试且安全的库来处理令牌验证或授权决策之类的事情。从零开始做所有事情意味着,除非你是安全专家,否则更容易把事情实现错。
- 使用短期有效的访问令牌。这取决于所使用的授权服务器,此设置可能是可自定义的。我们建议不要使用长期有效的令牌——如果恶意行为者窃取了它们,他们就能更长时间地维持访问权限。
- 始终验证令牌。你的服务器收到令牌,并不意味着该令牌有效,或它就是为你的服务器准备的。始终验证你的 MCP 服务器从客户端接收到的内容是否满足所需约束。
- 将令牌存储在安全、加密的存储中。在某些场景下,你可能需要在服务器端缓存令牌。如果是这种情况,请确保存储具备正确的访问控制,并且拥有你服务器访问权限的恶意方无法轻易将其外泄。你还应实现健壮的缓存驱逐策略,以确保你的 MCP 服务器不会重复使用已过期或其他无效的令牌。
- 在生产环境中强制使用 HTTPS。除开发期间的
localhost外,不要通过普通 HTTP 接受令牌或重定向回调。 - 最小权限范围。不要使用“包罗万象”的作用域。尽可能按工具或能力拆分访问,并在资源服务器上按路由/工具验证所需作用域。
- 不要记录凭据。绝不要记录
Authorization头、令牌、代码或密钥。清理查询字符串和头部。在结构化日志中对敏感字段进行脱敏。 - 分离应用与资源服务器凭据。不要在终端用户流程中复用你的 MCP 服务器客户端密钥。将所有密钥存放在合适的密钥管理器中,而不是源代码管理中。
- 返回正确的挑战信息。在 401 响应中,包含带有
Bearer、realm和resource_metadata的WWW-Authenticate,以便客户端可以发现如何进行身份验证。 - DCR(动态客户端注册)控制。如果启用,请注意与你的组织相关的特定约束,例如受信任的主机、必需的审查流程以及经过审计的注册。未认证的 DCR 意味着任何人都可以向你的授权服务器注册任意客户端。
- 多租户/领域混淆。除非明确支持多租户,否则请固定到单一签发者/租户。即使令牌由同一授权服务器签名,也要拒绝来自其他领域的令牌。
- 受众/资源标识符误用。不要配置或接受通用受众(如
api)或无关资源。要求受众/资源与你配置的服务器匹配。 - 错误详情泄露。向客户端返回通用消息,但在内部使用相关 ID 记录详细原因,以便排障,同时避免暴露内部信息。
- 会话标识符加固。将
Mcp-Session-Id视为不可信输入;切勿将授权绑定到它。请在认证变更时重新生成,并在服务器端验证其生命周期