跳到主要内容
版本:1.8.0

Entra 代理(On-Behalf-Of)令牌交换

有些上游 MCP 服务器和 REST API 按终端用户授权,并且只接受 Microsoft Entra ID 为它们签发的令牌。使用 auth_type: oauth2_token_exchange 和 token_exchange_profile: entra_obo 时,AISIX 会代表调用方获取该令牌:调用方登录 Entra 并出示签发给网关的 Entra 令牌,AISIX 验证该令牌,通过代理(On-Behalf-Of,OBO)流程在 Entra 换取新令牌,再把换得的令牌发送给上游。

上游只会收到换得的令牌,形式为 Authorization: Bearer <access_token>。调用方自己的令牌永远不会离开网关。

令牌交换适用于 type: mcp 和 type: openapi 服务器。

工作原理​

  1. 客户端以 Authorization: Bearer <token> 发送签发给网关应用注册的 Entra 访问令牌。
  2. JWT 信任服务提供方验证该令牌,并将其映射到一个调用方 API Key。该 Key 的 MCP 访问规则决定调用方能访问哪些服务器和工具。
  3. 对调用方可达的服务器,AISIX 向该服务器的 token_url 发送 OBO 请求。请求以表单编码,包含 grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer、作为 assertion 的调用方令牌、以空格连接的已配置 scopes(作为 scope)、requested_token_use=on_behalf_of,以及已配置的 client_id 和客户端密钥。
  4. AISIX 把 Entra 返回的访问令牌以 Authorization: Bearer <access_token> 发送给上游,并为同一调用方的后续请求缓存该令牌。

只有在同一请求中经 JWT 信任服务提供方验证过的令牌才会被交换。以其他方式认证的调用方没有可交换的令牌,AISIX 也不会为它访问令牌端点。这包括使用调用方 API Key 的调用方、匿名 MCP 入口,以及在 x-aisix-api-key 中发送网关 Key、同时在 Authorization 中携带 Entra 令牌的调用方。最后一种情况下,AISIX 认证的是该 Key,并不验证那个令牌。

前置条件​

开始前,请准备以下环境:

  • 为该服务器提供服务的每个环境中,网关均为 1.7.1 之后发布的版本。参见网关版本要求。
  • 完成 AISIX Enterprise、AISIX Cloud 或开源 AISIX 网关的设置 MCP 网关。
  • 一个 Microsoft Entra ID 租户,以及在其中注册应用和授予同意的权限。
  • 上游服务器所接受令牌对应的下游 API,已在 Entra 中注册,并至少有一个委托权限(scope)。
  • 一个能让用户登录 Entra,并获取签发给网关应用注册的访问令牌的客户端。OBO 流程只适用于用户令牌,因此通过客户端凭证获得的仅限应用的令牌无法交换。

在 Entra ID 中注册网关​

在 OBO 流程中,AISIX 是中间层 API:客户端为它申请令牌,它再为下游 API 申请令牌。Microsoft 的 On-Behalf-Of 流程文档是权威说明,以下步骤是 AISIX 所依赖的部分。

  1. 在 Microsoft Entra 管理中心为网关注册一个应用,记下其应用程序(客户端) ID 和目录(租户) ID。
  2. 在公开 API 中设置应用程序 ID URI,并添加一个范围,例如 access_as_user。客户端为该范围申请令牌,例如 api://<gateway-client-id>/access_as_user。Entra 只接受受众为发起请求的应用本身的令牌进行 OBO 交换,因此客户端的令牌必须签发给这个应用注册。
  3. 在证书和密码中创建客户端密码。AISIX 使用客户端 ID 和该密码向令牌端点认证。
  4. 在 API 权限中添加下游 API 的委托权限。
  5. 为该权限授予同意。租户管理员可以在同一页面授予管理员同意;也可以把客户端应用加入网关应用注册的 knownClientApplications,让用户在一次提示中同时同意两者。参见上述 OBO 流程文档中关于为中间层应用获得同意的说明。

租户的令牌端点为 https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token。

让网关信任 Entra 令牌​

AISIX 只能交换自己验证过的令牌。按照 JWT 认证中的说明,在环境中把该 Entra 租户注册为 JWT 信任服务提供方:

  • 将 issuer 设置为客户端所出示令牌的 iss 声明。对于 v2.0 访问令牌,其值为 https://login.microsoftonline.com/<tenant-id>/v2.0。
  • 将 audiences 设置为这些令牌的 aud 声明。对于 v2.0 访问令牌,其值为网关的应用程序(客户端) ID。

请解码客户端获得的一个样例令牌,并从中复制这两个值。Entra 会根据应用注册签发 v1.0 或 v2.0 令牌,两个版本的 iss 和 aud 不同。参见 Microsoft 的访问令牌声明参考。

然后把每个验证通过的身份映射到一个调用方 API Key:可以在 JWT 认证中把 Key 绑定到某个主体,也可以使用声明映射。为该 Key 授予所需的 MCP 工具,参见控制工具访问。

注册 MCP 服务器​

按下表配置服务器字段:

字段取值
auth_typeoauth2_token_exchange。
token_exchange_profileentra_obo,目前唯一支持的交换协议。
token_url租户的令牌端点。
client_id网关应用注册的应用程序(客户端) ID。
secret网关应用注册的客户端密码。
scopes下游 API 的至少一个范围,例如 api://<downstream-app-id>/.default。AISIX 用空格连接各条目。
timeout_ms可选,同时约束令牌交换;参见超时。

对该认证方式而言,除 timeout_ms 外的字段均为必填。

AISIX Enterprise 或 AISIX Cloud​

先导出上游身份认证中使用的控制面连接变量,然后创建服务器:

curl -sS -X POST "$AISIX_CP/mcp_servers" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "orders",
"url": "https://orders.example.com/mcp",
"auth_type": "oauth2_token_exchange",
"token_exchange_profile": "entra_obo",
"token_url": "https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token",
"client_id": "YOUR_GATEWAY_CLIENT_ID",
"secret": "YOUR_GATEWAY_CLIENT_SECRET",
"scopes": ["api://YOUR_DOWNSTREAM_APP_ID/.default"],
"allowed_environments": ["'"$ENV_ID"'"]
}'

❶ 使用 Entra 代理(On-Behalf-Of)交换。

❷ 租户的令牌端点和网关应用注册的凭证。客户端密码加密存储,不会再返回。

❸ 下游 API 的范围。

在控制台中,将服务器的认证方式选为令牌交换(Entra OBO)。表单会要求填写交换协议、客户端 ID、令牌端点 URL、客户端密钥和权限范围。编辑服务器时,客户端密钥留空即保留已存储的值。

开源 AISIX 网关​

把服务器添加到 mcp_servers,并通过环境变量插值提供客户端密码:

resources.yaml(Entra 代理令牌交换)
mcp_servers:
- name: orders
type: mcp
url: https://orders.example.com/mcp
auth_type: oauth2_token_exchange
token_exchange_profile: entra_obo
token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
client_id: YOUR_GATEWAY_CLIENT_ID
secret: ${ENTRA_OBO_CLIENT_SECRET}
scopes:
- api://YOUR_DOWNSTREAM_APP_ID/.default

同一文件还需要在 oidc_providers 中声明信任服务提供方,并在 api_keys 中声明绑定 JWT 的调用方 API Key,写法参见 JWT 认证。

连接客户端​

客户端把签发给网关的 Entra 令牌作为网关凭证发送:

curl -sS -X POST "$AISIX_PROXY/mcp/orders" \
-H "Authorization: Bearer $ENTRA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

在 /mcp/{server} 上,客户端一发送 initialize 或 server/discover,AISIX 就会执行令牌交换,因此客户端在第一个请求就能得知交换失败。

令牌缓存与重试​

AISIX 在网关进程内缓存换得的令牌,按调用方令牌、服务器和交换配置分别缓存。缓存的令牌会一直复用到 Entra 报告的 expires_in 到期前 60 秒;响应未提供 expires_in 时按 3,600 秒计算,且最长不超过 30 天。

同一调用方对同一服务器的并发请求如果都未命中缓存,会共用一次令牌端点请求,并得到相同的结果。交换失败不会被缓存,下一个请求会再次向 Entra 请求。

如果上游以 401 拒绝换得的令牌,AISIX 会丢弃缓存的令牌,重新交换,并用新令牌重试一次。Entra 的响应中没有可用的访问令牌,或 token_type 不是 Bearer 时,该响应不会被使用。

超时​

令牌交换受服务器的 timeout_ms 约束,与该服务器其他上游调用使用同一期限;未设置 timeout_ms 时为 30 秒。发往令牌端点的请求本身另有 30 秒上限,因此 timeout_ms 大于 30 秒并不会给交换更多时间。在 type: mcp 的工具调用或工具列表请求中,令牌交换与其后的上游连接共用一个期限。

超时的交换按 token_exchange_unavailable 失败处理。

错误​

在 /mcp/{server} 上,交换失败时返回 HTTP 状态码和 OpenAI 风格的错误体,其中 error.code 标明失败原因:

状态码error.code原因
401token_exchange_subject_missing调用方没有可交换的已验证令牌,例如它使用调用方 API Key 认证。
401token_exchange_subject_rejectedEntra 以下一行未列出的 4xx 响应拒绝了调用方的令牌。
500token_exchange_misconfiguredEntra 返回 invalid_client、unauthorized_client、unsupported_grant_type、invalid_target 或 invalid_scope,说明网关的应用注册、密码或范围配置有误。
503token_exchange_unavailableEntra 返回 429 或 408、返回 5xx、无法连接、未在期限内响应,或未返回可用的令牌。

Entra 返回的 error_description 不会传给调用方,也不会写入网关日志。

对于不在调用方授权范围内的服务器,AISIX 在 /mcp/{server} 上不会尝试交换。调用方得到的是普通的访问控制结果,例如空的 tools/list 或被拒绝的工具调用,而不是令牌交换错误。以末尾 * 覆盖整个服务器的拒绝规则(例如 orders__* 或 *)会把该服务器排除在授权范围之外。

在聚合的 /mcp 上,交换失败只影响该服务器:它的工具从 tools/list 中消失,调用其中某个工具会以工具错误失败。其他服务器照常工作。

Conditional Access 升级认证​

当环境发布了 MCP OAuth 发现时,两种 401 响应都会携带指向发现元数据的 WWW-Authenticate 质询:

WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://gateway.example.com/.well-known/oauth-protected-resource/mcp"

如果下游 API 要求的条件超出调用方令牌所能证明的范围,例如 Conditional Access 策略要求多重身份验证,Entra 会拒绝交换,并在拒绝中附带声明质询(claims challenge)。此时 AISIX 返回 401 token_exchange_subject_rejected,并通过升级认证质询把这些声明以 base64 编码交给客户端:

WWW-Authenticate: Bearer resource_metadata="https://gateway.example.com/.well-known/oauth-protected-resource/mcp", error="insufficient_claims", error_description="Step-up authentication required; satisfy the returned claims challenge with the IdP and retry", claims="eyJhY2Nlc3NfdG9rZW4iOnsiY2Fwb2xpZHMiOnsiZXNzZW50aWFsIjp0cnVlLCJ2YWx1ZXMiOlsiYzEiXX19fQ=="

客户端解码这些声明,携带它们向 Entra 申请新令牌,然后用新令牌重试。未启用 MCP OAuth 发现时,401 响应不携带 WWW-Authenticate 质询。

网关版本要求​

oauth2_token_exchange 需要 1.7.1 之后发布的网关。1.7.1 及更早版本的网关无法加载使用它的服务器。只要目标环境中注册的任一网关运行 1.7.1 或更早版本,AISIX Cloud Admin API 和控制台就会拒绝保存此类服务器,返回 422 和错误码 DP_INCOMPATIBLE。请先升级该环境中的网关。

后续步骤​