此示例展示了如何在Azure上部署一个受Entra ID保护的MCP服务器。
该示例还使用了一种授权模式,其中客户端首先获取用于MCP服务器的令牌,然后使用代为授权流将其交换为可用于Microsoft Graph的令牌。它完全以无密钥的方式完成这一切。
[!IMPORTANT] 这是一个实验性实现,不应在生产环境中使用。
此示例演示了如何通过实现OAuth 2.0动态客户端注册和PKCE流模式来构建受Entra ID保护的MCP服务器,这些模式可以解决当前Entra ID对这些标准的原生支持不足的问题。虽然它展示了技术可能性,但仅适用于教育和概念验证目的。
对于生产场景,请考虑使用已注册应用程序和标准OAuth流的成熟认证模式。
[!NOTE] 您可以使用模型上下文协议检查器或Visual Studio Code来测试这个MCP服务器。
此实现使用带有PKCE(用于代码交换的证明密钥)的复杂OAuth 2.0流程来安全地认证MCP客户端。以下是所有组件如何协同工作:
sequenceDiagram
participant Client as MCP Client
participant Browser as 用户的浏览器
participant APIM as API管理
participant Entra as Entra ID
participant Function as Azure函数
participant Graph as Microsoft Graph
Note over Client,Graph: 1. 发现与注册
Client->>APIM: GET /.well-known/oauth-authorization-server
APIM->>Client: OAuth元数据(authorization_endpoint, token_endpoint等)
Client->>APIM: POST /register (动态客户端注册)
Note over APIM: 在CosmosDB中存储客户端信息<br/>生成唯一的client_id
APIM->>Client: 客户端凭据(client_id,无密钥)
Note over Client,Graph: 2. 带有PKCE的授权
Note over Client: 生成code_verifier和code_challenge
Client->>Browser: 打开带有PKCE参数的授权URL
Browser->>APIM: GET /authorize?client_id=...&code_challenge=...
Note over APIM: 检查现有的同意cookie<br/>🍪 __Host-MCP_APPROVED_CLIENTS
alt 没有之前的同意
APIM->>Browser: 重定向到/consent页面
Note over Browser: 用户审查权限并点击“允许”
Browser->>APIM: POST /consent(带CSRF保护)
Note over APIM: 设置同意cookie<br/>🍪 __Host-MCP_APPROVED_CLIENTS<br/>🍪 __Host-MCP_CONSENT_STATE
end
APIM->>Browser: 重定向到Entra ID进行状态关联
Note over APIM: 设置Entra状态cookie<br/>🍪 __Host-MCP_ENTRA_STATE<br/>(包含sessionId|entraidState|clientState)
Browser->>Entra: 使用Entra ID进行授权
Note over Browser: 用户使用Entra ID进行身份验证
Entra->>APIM: 回调授权码
Note over APIM: 验证状态关联<br/>交换授权码为Entra访问令牌<br/>生成加密会话令牌
APIM->>Browser: 重定向带有MCP授权码
Note over Client,Graph: 3. 令牌交换
Browser->>APIM: POST /token (code + code_verifier)
Note over APIM: 验证PKCE挑战<br/>返回加密会话令牌
APIM->>Browser: 访问令牌(加密会话密钥)
Browser->>Client: 用于MCP通信的令牌
Note over Client,Graph: 4. 验证的MCP通信
Client->>APIM: GET /mcp/sse (带Bearer令牌)
Note over APIM: 解密会话令牌<br/>查找缓存的Entra令牌<br/>添加到下游请求
APIM->>Function: 转发带有Entra访问令牌
Function->>Graph: 使用代为授权流的API调用
Graph->>Function: 用户数据
Function->>APIM: MCP响应
APIM->>Client: 验证的MCP响应
此实现使用多种安全机制和Cookie以确保安全流程:
| Cookie | 目的 |
|---|---|
__Host-MCP_APPROVED_CLIENTS | 记住用户对特定客户端的同意 |
__Host-MCP_DENIED_CLIENTS | 记住拒绝的同意以防止重新提示 |
__Host-MCP_CONSENT_STATE | 验证同意表单提交 |
__Host-MCP_ENTRA_STATE | 关联Entra ID回调与原始请求 |
__Host-MCP_CSRF_TOKEN | 防止同意表单上的CSRF攻击 |
[!NOTE] 所有Cookie都使用
__Host-前缀,Secure,HttpOnly,并且SameSite=Lax以达到最大安全性(除了__Host-MCP_ENTRA_STATE,它使用SameSite=None以支持跨站点重定向)。
关键安全特性:
__Host-前缀以达到最大安全性按照以下步骤开始使用。
将仓库克隆到本地:
git clone https://github.com/localden/remote-auth-mcp-apim-py
导航到终端中的仓库:
cd remote-auth-mcp-apim-py
确保您的订阅已注册Microsoft.App资源提供程序,您可以在Azure门户或通过运行以下Azure CLI命令来执行此操作:
az login
az provider register --namespace Microsoft.App --wait
登录Azure开发者CLI:
azd auth login
将项目部署到Azure:
azd up
[!IMPORTANT] 部署此项目会产生Azure费用。如果您是为了测试和实验而部署,请确保在测试后删除创建的资源组。
当您运行azd up时,infra目录中声明的资源将在您的Azure帐户中被供应。您可以查看现有的Bicep文件以了解将自动部署的基础设施。

一旦部署完成,您将在终端中看到端点:

例如,在上述截图中,端点是https://apim-2lzunaz2nu642.azure-api.net/mcp/sse。复制它。
[!NOTE] 在下一步之前,请确保已安装Node.js - 它是运行模型上下文协议检查器所必需的。
在您的终端中运行:
npx @modelcontextprotocol/inspector@0.9.0
[!NOTE] 我们使用的是
0.9.0版本的模型上下文协议检查器,因为它是最稳定的版本,适合测试受保护的MCP服务器。
这将为您提供一个本地运行模型上下文协议检查器的端点。在浏览器中打开该URL。
切换传输类型到SSE并将URL设置为您从部署中获得的端点。点击连接。

您将被提示使用在您部署基础设施的租户中的凭据进行身份验证。Entra ID应用程序是在部署时动态注册的 - 一个是用于服务器,另一个将用于代为授权流以获取Microsoft Graph访问。
一旦您同意,您将返回到模型上下文协议检查器的首页。等待几秒钟直到连接建立 - 您将在页面上看到绿色的已连接标签。

一旦连接,点击列出工具并选择get_graph_user_details。这将使您能够从Microsoft Graph获取有关当前已认证用户的详细信息。点击运行工具。

如果一切顺利,您将在响应块中看到您的用户数据,如下所示:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "YOUR_NAME",
"givenName": null,
"jobTitle": null,
"mail": "YOUR_EMAIL",
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": null,
"userPrincipalName": "YOUR_UPN",
"id": "c6b77314-c0ec-44b2-b0bb-2c971a753f0c",
"success": true
}
如果您遇到任何障碍或有任何意见,请确保打开一个问题。