API管理×MCP×OAuth端到端MCP保护
该存储库为使用API Management×MCP×OAuth实现端到端保护提供了实践学习体验。
实践概述
这个动手实验室演示了以两种模式安全执行MCP的端到端流程:
- AI代理/Claude代码→ APIM → MCP
- VS代码(GitHub复制聊天/CLI)→ APIM → MCP
AI代理/Claude代码→ APIM → MCP
从AI代理(例如MS Foundry代理)或Claude代码执行MCP时的总体流程。\ 两个客户端都提前获得访问令牌(通过托管身份、服务主体或 az login)并将其附加到请求中——APIM流是相同的。
sequenceDiagram
participant Agent as AI Agent / Claude Code
participant Entra as Entra ID
participant APIM as API Management
participant MCP as MCP Backend (Logic Apps / Azure Functions)
Agent->>Entra: 1) Request access token
Entra-->>Agent: 2) Return access token
Agent->>APIM: 3) Access /mcp with access token
Note over APIM: 4) Validate access token
APIM->>Entra: 5a) Acquire token for MCP Backend using APIM Managed Identity
APIM->>MCP: 5b) Access MCP backend with APIM MSI token
MCP-->>APIM: 6a) Return tool list
APIM-->>Agent: 6b) Return tool list
Agent->>APIM: 7) Request to execute MCP tool
Note over APIM: 8) Validate MCP role from access token
alt 9a) Role validation succeeds
APIM->>MCP: 9a-1) Execute MCP tool with APIM MSI token
MCP-->>APIM: 9a-2) Tool execution result
APIM-->>Agent: 9a-3) Return MCP tool execution result
else 9b) Role validation fails
APIM-->>Agent: 403 Forbidden
endVS代码(GitHub复制聊天/CLI)→ APIM → MCP
从VS Code(GitHub Copilot Chat或CLI)执行MCP时的总体流程。\ VS Code中的Chat和CLI共享相同的OAuth授权流——它们从APIM发现OAuth端点并进行交互身份验证。
sequenceDiagram
participant VS as VS Code (GitHub Copilot Chat / CLI)
participant APIM as API Management
participant ID as Entra ID
participant MCP as MCP Backend (Logic Apps / Azure Functions)
VS->>APIM: 1) Request access to /mcp
APIM-->>VS: 2) 401 Unauthorized
VS->>APIM: 3) GET /.well-known/oauth-protected-resource
APIM-->>VS: 4) Return OAuth metadata
VS->>ID: 5) Get an access token through the login/authorization code flow
VS->>APIM: 6) Re-access /mcp with access token
Note over APIM: 7) Validate access token
APIM->>ID: 8a) Acquire token for MCP Backend using APIM Managed Identity
APIM->>MCP: 8b) Access MCP backend with APIM MSI token
MCP-->>APIM: 9a) Return tool list
APIM-->>VS: 9b) Return tool list
VS->>APIM: 10) Request to execute MCP tool
Note over APIM: 11) Validate MCP role from access token
alt 12a) Role validation succeeds
APIM->>MCP: 12a-1) Execute MCP tool with APIM MSI token
MCP-->>APIM: 12a-2) Tool execution result
APIM-->>VS: 12a-3) Return MCP tool execution result
else 12b) Role validation fails
APIM-->>VS: 403 Forbidden
end架构特征
这个动手实验室通过以下五个要素实现了强大的端到端安全:
1.OAuth2授权
通过Entra ID进行用户身份验证和访问令牌发放。
2.令牌验证
APIM验证OAuth访问令牌(颁发者/受众/签名/过期)并阻止未经授权的请求。
3.基于角色的授权
评估访问令牌中的MCP角色(声明),以实现细粒度的工具执行控制。
4.秘密连接
APIM的托管身份(MSI)实现了对MCP后端的安全访问,消除了凭证泄漏风险。
5.后端保护
MCP后端使用Easy Auth只允许APIM的MSI,防止直接外部访问。
部署动手环境
使用Azure Developer CLI(azd)按照以下步骤部署环境。
先决条件
确保在本地安装以下内容:
- Terraform(推荐:v1.5或更高版本)
- Azure开发者命令行界面(
azd,推荐:v1.9或更高版本)
部署步骤
1.构建MCP应用程序UI
构建Azure Functions用作MCP Apps UI的前端小部件。
cd src/funcmcp/app
npm install
npm run build
cd -2.部署
azd up删除资源
azd down动手的
AI代理/Claude代码→ APIM → MCP
重现AI代理(例如MS Foundry代理)或Claude Code调用MCP的操作序列。\ 两个客户端都通过预先获取Azure访问令牌进行身份验证——AI代理使用Python脚本,Claude Code使用 .mcp.json 和 headersHelper.
sequenceDiagram
participant Agent as AI Agent / Claude Code
participant Entra as Entra ID
participant APIM as API Management
participant MCP as MCP Backend (Logic Apps / Azure Functions)
Agent->>Entra: 1) Request access token
Entra-->>Agent: 2) Return access token
Agent->>APIM: 3) Access /mcp with access token
Note over APIM: 4) Validate access token
APIM->>Entra: 5a) Acquire token for MCP Backend using APIM Managed Identity
APIM->>MCP: 5b) Access MCP backend with APIM MSI token
MCP-->>APIM: 6a) Return tool list
APIM-->>Agent: 6b) Return tool list
Agent->>APIM: 7) Request to execute MCP tool
Note over APIM: 8) Validate MCP role from access token
alt 9a) Role validation succeeds
APIM->>MCP: 9a-1) Execute MCP tool with APIM MSI token
MCP-->>APIM: 9a-2) Tool execution result
APIM-->>Agent: 9a-3) Return MCP tool execution result
else 9b) Role validation fails
APIM-->>Agent: 403 Forbidden
end步骤
1.设置Python环境
导航到 samplecodes/ 目录,创建并激活Python虚拟环境,并安装所需的Python包。
cd samplecodes
python -m venv oauthmcp
source oauthmcp/bin/activate
pip install azure.identity2.配置环境变量
从azd环境中动态设置所需的值。
export OAUTH_APP_ID="$(azd env get-value OAUTH_APP_ID)"
export MCP_URL="$(azd env get-value LOGICAPP_MCP_ENDPOINTS)"3.验证访问令牌
跑 check.entraid_token.py 并验证 hello_project1 包含在 roles 索赔。
python check.entraid_token.py | grep -v '^===' \
| jq 'if .roles then (if (.roles | any(. == "hello_project1")) then "✅ OK: hello_project1 is included in roles" else "❌ NG: hello_project1 not found in roles — run az logout && az login" end) else "⚠️ roles claim is missing — run az logout && az login" end'4.检索MCP工具列表
跑 mcp_tool_list.py 以验证是否可以检索工具列表。
python mcp_tool_list.py5.执行MCP工具(成功案例)
执行 hello_project1 工具并确认成功。
export MCP_TOOL_NAME="hello_project1"
python mcp_tool_call.py6.执行MCP工具(拒收案例)
执行 hello_project2 工具,并确认由于缺乏权限而被拒绝。
export MCP_TOOL_NAME="hello_project2"
python mcp_tool_call.py克劳德代码
Claude Code使用相同的基于令牌的流连接到受APIM保护的MCP服务器。\ 这 headersHelper 领域 .mcp.json 电话 az account get-access-token 在启动时,因此不需要交互式浏览器登录。
提示 为什么选择Azure CLI令牌而不是OAuth客户端凭据?\ Claude Code通过动态客户端注册(DCR)原生支持OAuth授权。但是,Entra ID不支持DCR。静态客户端注册是一种替代方案,但它需要向每个用户分发客户端密钥,这会带来安全风险。作为一种实用的解决方法,可以将Claude Code配置为通过Azure CLI获取令牌(az login)使用headersHelper相反。 访问令牌到期和重新连接\ Azure AD访问令牌的有效期为1小时。headersHelper仅在Claude Code启动或MCP连接重新建立时运行,它不会在会话中自动刷新令牌。如果令牌在会话期间过期,请重新启动Claude Code或重新连接MCP服务器以获取新的令牌。 为什么预先授权Azure CLI是安全的\ 这headersHelper命令获取此动手操作的Entra ID应用程序范围内的令牌(api://)使用Azure CLI客户端ID(04b07795-8ddb-461a-bbee-02f9e1bf7b46).这与微软用于其自己的第一方服务的模式相同(例如。,https://ai.azure.com)--它们根据Entra ID预先授权,并以相同的方式通过Azure CLI获取令牌。此外,此处注册的OAuth应用程序是单租户,因此只有可以登录此特定租户的用户才能获得令牌。实现此功能的Terraform资源是: ``hcl resource "azuread_application_pre_authorized" "oauth_app" { application_id = azuread_application.oauth_app.id authorized_client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" # Azure CLI permission_ids = [ azuread_application_permission_scope.user_impersonation.scope_id, ] }``
1.配置 .mcp.json
生成 .mcp.json 动态使用 azd env get-value.
cd $(git rev-parse --show-toplevel)
FUNC_URL="$(azd env get-value FUNC_MCP_ENDPOINTS)"
LA_URL="$(azd env get-value LOGICAPP_MCP_ENDPOINTS)"
OAUTH_APP_ID="$(azd env get-value OAUTH_APP_ID)"
HELPER="TOKEN=\$(az account get-access-token --scope 'api://${OAUTH_APP_ID}/.default' --query accessToken -o tsv) && echo \"{\\\"Authorization\\\": \\\"Bearer \$TOKEN\\\"}\""
jq -n --arg func_url "$FUNC_URL" --arg la_url "$LA_URL" --arg helper "$HELPER" \
'{mcpServers: {
"func-hello-mcp": {type: "http", url: $func_url, headersHelper: $helper},
"logic-hello-mcp": {type: "http", url: $la_url, headersHelper: $helper}
}}' \
> .mcp.json3.执行MCP工具(成功案例)
在克劳德代码中提示以下内容,并确认MCP工具成功执行。
Use func-hello-mcp to say hello to project14.执行MCP工具(拒收案例)
提示以下内容,并确认由于缺乏权限而拒绝执行。
Use func-hello-mcp to say hello to project2VS代码(GitHub复制聊天/CLI)→ APIM → MCP
验证VS Code(GitHub Copilot Chat或CLI)调用MCP的操作顺序。
sequenceDiagram
participant VS as VS Code (GitHub Copilot Chat / CLI)
participant APIM as API Management
participant ID as Entra ID
participant MCP as MCP Backend (Logic Apps / Azure Functions)
VS->>APIM: 1) Request access to /mcp
APIM-->>VS: 2) 401 Unauthorized
VS->>APIM: 3) GET /.well-known/oauth-protected-resource
APIM-->>VS: 4) Return OAuth metadata
VS->>ID: 5) Get an access token through the login/authorization code flow
VS->>APIM: 6) Re-access /mcp with access token
Note over APIM: 7) Validate access token
APIM->>ID: 8a) Acquire token for MCP Backend using APIM Managed Identity
APIM->>MCP: 8b) Access MCP backend with APIM MSI token
MCP-->>APIM: 9a) Return tool list
APIM-->>VS: 9b) Return tool list
VS->>APIM: 10) Request to execute MCP tool
Note over APIM: 11) Validate MCP role from access token
alt 12a) Role validation succeeds
APIM->>MCP: 12a-1) Execute MCP tool with APIM MSI token
MCP-->>APIM: 12a-2) Tool execution result
APIM-->>VS: 12a-3) Return MCP tool execution result
else 12b) Role validation fails
APIM-->>VS: 403 Forbidden
end步骤
1.启动MCP
当提示进行Entra ID身份验证时,通过浏览器进行身份验证,并验证是否正确检索到工具列表。
2.执行MCP工具(成功案例)
GitHub Copilot聊天(代理模式): 提示以下内容并确认MCP工具成功执行。
Use func-hello-mcp to say hello to project1GitHub Copilot命令行界面: 启动Copilot CLI交互会话。
copilot在交互会话中,发送以下消息并确认MCP工具成功执行。
Use func-hello-mcp to say hello to project13.执行MCP工具(拒收案例)
GitHub Copilot聊天(代理模式): 提示以下内容,并确认由于缺乏权限而拒绝执行。
Use func-hello-mcp to say hello to project2GitHub Copilot命令行界面: 在交互会话中,发送以下消息并确认执行因缺乏权限而被拒绝。
Use func-hello-mcp to say hello to project2技术细节和用例
结论
该存储库结合API管理×MCP×OAuth,提供了端到端安全实施的实践经验。
人工智能安全方法
在人工智能安全方面,我认为“基于零信任原则的入口点严格认证和授权”是主动防范风险的最关键因素。
然而,安全性和便利性总是处于一种权衡关系中。虽然这个世界经常以二元术语进行讨论,但在实际操作中实现适当的平衡至关重要。
在这个实践实验室中,我们结合了以下设计考虑因素,以平衡便利性和安全性:
- 授权范围:我们没有阻止所有内容,而是将基于角色的授权专门集中在MCP工具调用上
- 集成身份验证流程:旨在直接利用VS Code中的Microsoft帐户令牌进行MCP授权,而不会影响开发人员体验
“在入口点在哪里进行防御,在哪里提供用户体验”——这一决定是实际安全设计的核心。
为了做出这样的设计决策,不仅要从官方文件和其他来源获取外部知识(外向性),还要深入了解自己(内向性),探索自己的解决方案。仅仅从外部寻求答案不会导致最佳架构。
最后的思考
安全应该是 “防止误用的安全机制”,而不是“对用户和开发人员的限制” 在这个实践实验室中实施的安全功能旨在作为这种积极的安全机制发挥作用。
我希望这个存储库能为您在使用AI代理和MCP的系统中的安全设计提供有用的资源。
