Skip to content

Commit c64c45d

Browse files
authored
Merge pull request #782 from agentgateway/adb-fixes
Docs for MS Entra in MCP auth
2 parents fcea65f + 2fb17cf commit c64c45d

6 files changed

Lines changed: 707 additions & 3 deletions

File tree

assets/agw-docs/pages/agentgateway/llm/providers/backend-tunnel-proxy.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ This example configures an {{< reuse "agw-docs/snippets/agentgateway.md" >}} bac
4646
port: 443
4747
policies:
4848
tls:
49-
insecureSkipVerify: true
49+
insecureSkipVerify: All
5050
tunnel:
5151
backendRef:
5252
group: {{< reuse "agw-docs/snippets/group.md" >}}
@@ -61,6 +61,6 @@ This example configures an {{< reuse "agw-docs/snippets/agentgateway.md" >}} bac
6161
| `spec.static.host` | The upstream backend host (the actual service you want to reach) |
6262
| `spec.policies.tunnel.backendRef.name` | The {{< reuse "agw-docs/snippets/backend.md" >}} or `Service` that represents the proxy |
6363
| `spec.policies.tunnel.backendRef.port` | The port the proxy listens on |
64-
| `spec.policies.tls.insecureSkipVerify` | Skip TLS certificate validation when connecting to the upstream backend. Use with caution — only in trusted environments. |
64+
| `spec.policies.tls.insecureSkipVerify` | Skip TLS certificate validation when connecting to the upstream backend. Set to `All` to skip all verification or `Hostname` to skip only hostname verification. Use with caution — only in trusted environments. |
6565

6666
3. Agentgateway now routes all outbound connections from `idp-proxied` through the `squid-proxy` tunnel. When the control plane needs to fetch JWKS keys, it does so via the proxy.
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
Secure your Model Context Protocol (MCP) servers with OAuth 2.0 authentication by using agentgateway and Microsoft Entra ID (formerly Azure Active Directory) as the identity provider.
2+
3+
## About this guide
4+
5+
In this guide, you configure the agentgateway proxy to protect a static MCP server with MCP auth by using Microsoft Entra ID as the authorization server. Because Entra does not fully implement the OAuth behaviors that the [MCP authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) assumes, agentgateway includes a native `Entra` provider that bridges the gaps. When you set `provider: Entra`, agentgateway serves RFC 8414 authorization server metadata from Entra's OIDC discovery document, strips the RFC 8707 `resource` parameter that Entra rejects, and short-circuits Dynamic Client Registration with your pre-registered application (client) ID.
6+
7+
{{< callout type="warning" >}}
8+
This guide configures Entra app registrations that are **public clients** using PKCE, such as local MCP clients. Confidential clients (app registrations under the Entra **Web** platform) require a client secret at the token endpoint. On Kubernetes, injecting that secret through the recommended `jwtAuthentication.mcp` traffic policy is not yet supported. If you need confidential-client support today, use agentgateway in standalone mode, which accepts a `clientSecret` field on the MCP authentication policy.
9+
{{< /callout >}}
10+
11+
For more information about MCP auth, see the [About MCP auth]({{< link-hextra path="/mcp/auth/about/" >}}) page.
12+
13+
## Before you begin
14+
15+
1. Set up an [agentgateway proxy]({{< link-hextra path="/setup/gateway/" >}}).
16+
2. Follow the steps to set up an [MCP server with a fetch tool]({{< link-hextra path="/mcp/static-mcp/" >}}).
17+
3. Install the experimental channel Gateway API.
18+
```sh {paths="setup-entra"}
19+
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v{{< reuse "agw-docs/versions/k8s-gw-version-exp.md" >}}/experimental-install.yaml
20+
```
21+
4. Register an application in Microsoft Entra ID and collect the values that agentgateway needs.
22+
1. Make sure that you have access to a [Microsoft Entra ID tenant](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-create-new-tenant). If you do not have one, you can create a free tenant for development purposes.
23+
2. [Register an application](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) in the Microsoft Entra admin center. For **Supported account types**, choose the option that fits your organization. Under **Redirect URI**, select the **Mobile and desktop applications** platform and add the callback URLs of the MCP clients that you plan to connect.
24+
3. From the app's **Overview** page, note the **Directory (tenant) ID** and the **Application (client) ID**, and save them as environment variables.
25+
```bash
26+
export ENTRA_TENANT_ID=<your-tenant-id>
27+
export ENTRA_CLIENT_ID=<your-application-client-id>
28+
```
29+
4. Select **Expose an API**. Next to **Application ID URI**, click **Set** and accept the default value of `api://${ENTRA_CLIENT_ID}`. Then click **Add a scope**, enter a scope name such as `mcp_access`, set **Who can consent** to **Admins and users**, and click **Add scope**.
30+
31+
{{< doc-test paths="setup-entra" >}}
32+
# The controller fetches the provider's remote JWKS when it translates the policy,
33+
# so the test uses Microsoft's real multi-tenant `common` endpoint
34+
# (https://login.microsoftonline.com/common/discovery/v2.0/keys) to resolve keys
35+
# without a dedicated tenant. The client ID is a placeholder; it is not validated
36+
# for the unauthenticated requests the test makes. Replace both with your real
37+
# tenant and app registration IDs when you follow the guide.
38+
export ENTRA_TENANT_ID="${ENTRA_TENANT_ID:-common}"
39+
export ENTRA_CLIENT_ID="${ENTRA_CLIENT_ID:-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}"
40+
{{< /doc-test >}}
41+
42+
## Create the JWKS backend
43+
44+
Create a {{< reuse "agw-docs/snippets/backend.md" >}} that points to the Microsoft login endpoint, and a BackendTLSPolicy that originates a TLS connection to it. The JWT authentication policy uses this backend to fetch Entra's public keys for token signature validation.
45+
46+
1. Create an {{< reuse "agw-docs/snippets/backend.md" >}} for the Microsoft login endpoint.
47+
```yaml {paths="setup-entra"}
48+
kubectl apply -f- <<EOF
49+
apiVersion: {{< reuse "agw-docs/snippets/api-version.md" >}}
50+
kind: {{< reuse "agw-docs/snippets/backend.md" >}}
51+
metadata:
52+
name: entra-jwks
53+
spec:
54+
static:
55+
host: login.microsoftonline.com
56+
port: 443
57+
EOF
58+
```
59+
60+
2. Create a BackendTLSPolicy that originates a TLS connection to the `entra-jwks` backend by using well-known trusted CA certificates.
61+
```yaml {paths="setup-entra"}
62+
kubectl apply -f- <<EOF
63+
apiVersion: gateway.networking.k8s.io/v1
64+
kind: BackendTLSPolicy
65+
metadata:
66+
name: entra-jwks
67+
spec:
68+
targetRefs:
69+
- name: entra-jwks
70+
kind: {{< reuse "agw-docs/snippets/backend.md" >}}
71+
group: agentgateway.dev
72+
validation:
73+
hostname: login.microsoftonline.com
74+
wellKnownCACertificates: System
75+
EOF
76+
```
77+
78+
## Configure MCP auth
79+
80+
With your MCP backend configured, create an {{< reuse "agw-docs/snippets/policy.md" >}} that enforces Entra authentication for the MCP backend.
81+
82+
1. Create an {{< reuse "agw-docs/snippets/policy.md" >}} with MCP authentication configuration. MCP authentication is configured at the route level by using `traffic.jwtAuthentication` with the `mcp` extension field. The route-level placement aligns MCP auth with standard JWT authentication and lets you use JWT claims in other route-level policies, such as authorization, rate limiting, and transformations.
83+
```yaml {paths="setup-entra"}
84+
kubectl apply -f - <<EOF
85+
apiVersion: {{< reuse "agw-docs/snippets/api-version.md" >}}
86+
kind: {{< reuse "agw-docs/snippets/policy.md" >}}
87+
metadata:
88+
name: mcp-entra-authn
89+
spec:
90+
# Target the HTTPRoute to apply authentication at the route level
91+
targetRefs:
92+
- group: gateway.networking.k8s.io
93+
kind: HTTPRoute
94+
name: mcp
95+
traffic:
96+
jwtAuthentication:
97+
mode: Strict
98+
providers:
99+
# v2 issuer form. The v1 form https://sts.windows.net/<tenant-id>/ is
100+
# also supported; use it when the app registration mints v1 tokens.
101+
- issuer: "https://login.microsoftonline.com/${ENTRA_TENANT_ID}/v2.0"
102+
# List both the api://<client-id> and bare <client-id> audience
103+
# formats to accept the aud claim that Entra mints for v1 and v2 tokens.
104+
audiences:
105+
- "api://${ENTRA_CLIENT_ID}"
106+
- "${ENTRA_CLIENT_ID}"
107+
jwks:
108+
remote:
109+
backendRef:
110+
name: entra-jwks
111+
kind: {{< reuse "agw-docs/snippets/backend.md" >}}
112+
group: agentgateway.dev
113+
port: 443
114+
jwksPath: "/${ENTRA_TENANT_ID}/discovery/v2.0/keys"
115+
mcp:
116+
# Use the native Entra provider to bridge Entra's OAuth behaviors
117+
provider: Entra
118+
# Entra has no Dynamic Client Registration, so the gateway answers
119+
# registration requests with this pre-registered client ID.
120+
clientId: "${ENTRA_CLIENT_ID}"
121+
resourceMetadata:
122+
resource: http://localhost:8080/mcp
123+
scopesSupported:
124+
- "api://${ENTRA_CLIENT_ID}/mcp_access"
125+
bearerMethodsSupported:
126+
- header
127+
EOF
128+
```
129+
130+
{{< reuse "agw-docs/snippets/review-table.md" >}} For more information about the `traffic.jwtAuthentication` field, see the [API docs]({{< link-hextra path="/reference/api/#jwtauthentication" >}}).
131+
132+
| Setting | Description |
133+
| -- | -- |
134+
| `providers[].issuer` | The Entra token issuer URL. Use the v2 form `https://login.microsoftonline.com/<tenant-id>/v2.0` or the v1 form `https://sts.windows.net/<tenant-id>/`, depending on which version your app registration mints. This value must match the `iss` claim in the token. |
135+
| `providers[].audiences` | The accepted audiences. List both `api://<client-id>` and the bare `<client-id>` to accept the `aud` claim formats that Entra mints for v1 and v2 tokens. |
136+
| `providers[].jwks.remote.backendRef` | The `entra-jwks` backend that points to `login.microsoftonline.com`. |
137+
| `providers[].jwks.remote.jwksPath` | The path to Entra's JWKS endpoint for your tenant. |
138+
| `mcp.provider` | The identity provider. Set to `Entra` to enable the native Entra bridging behavior. |
139+
| `mcp.clientId` | The Application (client) ID of your Entra app registration. Because Entra has no Dynamic Client Registration, agentgateway answers registration requests with this value. |
140+
| `mcp.resourceMetadata` | MCP OAuth resource metadata for discovery. Includes the resource identifier, supported scopes, and bearer token methods. |
141+
142+
2. Verify that the policy was accepted.
143+
```sh {paths="setup-entra"}
144+
kubectl get {{< reuse "agw-docs/snippets/policy.md" >}} mcp-entra-authn -o yaml
145+
```
146+
147+
{{< doc-test paths="setup-entra" >}}
148+
YAMLTest -f - <<'EOF'
149+
- name: wait for entra-jwks BackendTLSPolicy to be accepted
150+
wait:
151+
target:
152+
kind: BackendTLSPolicy
153+
metadata:
154+
namespace: default
155+
name: entra-jwks
156+
jsonPath: "$.status.ancestors[0].conditions[?(@.type=='Accepted')].status"
157+
jsonPathExpectation:
158+
comparator: equals
159+
value: "True"
160+
polling:
161+
timeoutSeconds: 60
162+
intervalSeconds: 2
163+
- name: wait for mcp-entra-authn policy to be accepted
164+
wait:
165+
target:
166+
kind: AgentgatewayPolicy
167+
metadata:
168+
namespace: default
169+
name: mcp-entra-authn
170+
jsonPath: "$.status.ancestors[0].conditions[?(@.type=='Accepted')].status"
171+
jsonPathExpectation:
172+
comparator: equals
173+
value: "True"
174+
polling:
175+
timeoutSeconds: 60
176+
intervalSeconds: 2
177+
EOF
178+
{{< /doc-test >}}
179+
180+
3. Update the HTTPRoute that routes incoming traffic to the MCP server to include the discovery paths for the MCP resource and authorization server. This way, the agentgateway proxy can retrieve the resource and authorization server metadata during the MCP auth flow. The authorization server path uses a prefix match so that the proxy can serve the bridged Entra metadata and proxy the `authorize` and `token` endpoints under it.
181+
```yaml {paths="setup-entra"}
182+
kubectl apply -f - <<EOF
183+
apiVersion: gateway.networking.k8s.io/v1
184+
kind: HTTPRoute
185+
metadata:
186+
name: mcp
187+
spec:
188+
parentRefs:
189+
- group: gateway.networking.k8s.io
190+
kind: Gateway
191+
name: agentgateway-proxy
192+
namespace: {{< reuse "agw-docs/snippets/namespace.md" >}}
193+
rules:
194+
- filters:
195+
- type: CORS
196+
cors:
197+
allowCredentials: true
198+
allowHeaders:
199+
- Origin
200+
- Authorization
201+
- Content-Type
202+
allowMethods:
203+
- "*"
204+
allowOrigins:
205+
- "*"
206+
exposeHeaders:
207+
- Origin
208+
- X-HTTPRoute-Header
209+
maxAge: 86400
210+
backendRefs:
211+
- group: agentgateway.dev
212+
kind: {{< reuse "agw-docs/snippets/backend.md" >}}
213+
name: mcp-backend
214+
matches:
215+
# Main MCP endpoint to connect to the MCP server
216+
- path:
217+
type: PathPrefix
218+
value: /mcp
219+
# Path to access resource server metadata
220+
- path:
221+
type: PathPrefix
222+
value: /.well-known/oauth-protected-resource/mcp
223+
# Path to access the bridged authorization server metadata and proxied endpoints
224+
- path:
225+
type: PathPrefix
226+
value: /.well-known/oauth-authorization-server/mcp
227+
EOF
228+
```
229+
230+
{{< doc-test paths="setup-entra" >}}
231+
# WHAT THIS TEST VALIDATES:
232+
# * The Entra MCP auth resources (entra-jwks backend + BackendTLSPolicy, the
233+
# mcp-entra-authn AgentgatewayPolicy with provider: Entra and clientId, and the
234+
# updated HTTPRoute) are accepted, and the provider's JWKS resolves from the
235+
# Entra `common` endpoint so the policy programs on the data plane.
236+
# * The gateway enforces the connect-time 401 challenge and serves the
237+
# protected-resource metadata.
238+
# WHAT THIS TEST DOES NOT VALIDATE (and why):
239+
# * The full interactive OAuth sign-in flow requires a real user signing in to a
240+
# configured Entra app registration, which an automated test cannot perform.
241+
YAMLTest -f - <<'EOF'
242+
- name: wait for mcp HTTPRoute to be accepted
243+
wait:
244+
target:
245+
kind: HTTPRoute
246+
metadata:
247+
namespace: default
248+
name: mcp
249+
jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status"
250+
jsonPathExpectation:
251+
comparator: equals
252+
value: "True"
253+
polling:
254+
timeoutSeconds: 60
255+
intervalSeconds: 2
256+
- name: unauthenticated MCP request returns 401 (connect-time auth enforced)
257+
http:
258+
url: "http://${INGRESS_GW_ADDRESS}:80/mcp"
259+
method: GET
260+
source:
261+
type: local
262+
expect:
263+
statusCode: 401
264+
headers:
265+
- name: www-authenticate
266+
comparator: contains
267+
value: resource_metadata
268+
retries: 3
269+
- name: resource metadata discovery returns 200
270+
http:
271+
url: "http://${INGRESS_GW_ADDRESS}:80/.well-known/oauth-protected-resource/mcp"
272+
method: GET
273+
source:
274+
type: local
275+
expect:
276+
statusCode: 200
277+
bodyJsonPath:
278+
- path: "$.resource"
279+
comparator: contains
280+
value: "/mcp"
281+
retries: 3
282+
EOF
283+
{{< /doc-test >}}
284+
285+
## Verify MCP auth
286+
287+
Verify the auth flow with the [MCP inspector](https://github.com/modelcontextprotocol/inspector). Because the flow redirects you to Microsoft to sign in, this verification is interactive and requires a live Entra tenant.
288+
289+
1. Open the MCP inspector.
290+
```sh
291+
npx @modelcontextprotocol/inspector@{{% reuse "agw-docs/versions/mcp-inspector.md" %}}
292+
```
293+
294+
2. From the MCP Inspector menu, connect to your agentgateway address:
295+
* **Transport Type**: Select `Streamable HTTP`.
296+
* **URL**: Enter the agentgateway address and the `/mcp` path. For a LoadBalancer, use `http://${INGRESS_GW_ADDRESS}/mcp`. For a port-forwarded proxy, use `http://localhost:8080/mcp`.
297+
* Click **Connect**. Verify that the connection fails because authentication is required.
298+
299+
3. Click **Open Auth Settings** and run through the OAuth flow. During the flow, the MCP inspector discovers the bridged authorization server metadata, registers with your pre-configured `clientId`, and redirects you to Microsoft to sign in. After you sign in and the token is issued, agentgateway validates the Entra token and completes the connection.
300+
301+
4. Verify that tool calls work without re-authentication. From the **Tools** tab, click **List Tools**, select the `fetch` tool, enter a URL such as `https://example.com/`, and click **Run Tool**. The call succeeds because the token from the initial connection is reused for all tool calls within the session.
302+
303+
## Clean up
304+
305+
{{< reuse "agw-docs/snippets/cleanup.md" >}}
306+
307+
```sh
308+
kubectl delete {{< reuse "agw-docs/snippets/policy.md" >}} mcp-entra-authn
309+
kubectl delete backendtlspolicy entra-jwks
310+
kubectl delete {{< reuse "agw-docs/snippets/backend.md" >}} entra-jwks
311+
kubectl delete httproute mcp
312+
```

assets/agw-docs/pages/integrations/vllm-semantic-router.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The following diagram shows the [cost-based routing example](/blog/2026-07-17-se
1212
The request follows these component boundaries:
1313

1414
1. A client sends a supported request to agentgateway.
15-
2. An AgentgatewayPolicy calls vSR as an external processor during the `PreRouting` phase.
15+
2. An {{< reuse "agw-docs/snippets/policy.md" >}} calls vSR as an external processor during the `PreRouting` phase.
1616
3. vSR evaluates its semantic, complexity, keyword, context, and structure signals. It returns the selected model in its processing response.
1717
4. Agentgateway applies the routing decision and forwards the request to the configured provider or inference workload.
1818
5. Agentgateway records the requested and selected models alongside usage, latency, and optional catalog-priced cost data.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: Set up Microsoft Entra ID
3+
weight: 50
4+
description: Configure Microsoft Entra ID (Azure AD) as an OAuth identity provider for MCP authentication with agentgateway.
5+
test:
6+
setup-entra:
7+
- file: ${versionRoot}/install/helm.md
8+
path: experimental
9+
- file: ${versionRoot}/setup/gateway.md
10+
path: all
11+
- file: ${versionRoot}/mcp/static-mcp.md
12+
path: setup-mcp-server
13+
- file: ${versionRoot}/mcp/auth/entra.md
14+
path: setup-entra
15+
---
16+
17+
{{< reuse "agw-docs/pages/agentgateway/mcp/mcp-auth-entra.md" >}}

0 commit comments

Comments
 (0)