Skip to content

Conversation

@jmcguire98
Copy link
Contributor

@jmcguire98 jmcguire98 commented Nov 12, 2025

Currently, agentgateway only supports MCP authentication configured locally.

Under the hood, local config creates a route-level JWT policy for MCP authentication. We can't take this same approach when configured via xDS, since config arrives dynamically and expecting the control plane to configure route-level policies for all routes that reference an MCP backend would be quite complex in practice.

Instead, I've implemented JWT validation for MCP authentication similar to how backend policies work. MCP Authentication now includes JWT verification directly, which avoids the introduction of a backend JWT policy that only works with MCP backends.

I've also updated the MCP authentication proto/xds code to expect inline JWKS, since we expect the control plane to handle JWKS fetch.

Since MCP backends seem to bypass the normal apply_backend_policies flow (they return early in make_backend_call), JWT validation is currently applied directly in the MCP router.

@jmcguire98 jmcguire98 changed the title [WIP] feat: Support MCP Authn when configured by xDS feat: Support MCP Authn when configured by xDS Nov 14, 2025
Copy link
Collaborator

@howardjohn howardjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG!

return Self::create_auth_required_response(&req, auth).into_response();
},
// if no mcp authn is configured or JWT already validated (claims exist), do nothing
_ => {},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... so in theory now I can have 3 JWT policies: Gateway Route and MCPBackend.

The first 2 do not have this "allow if previous already did it". Maybe it should given its impossible for it to pass if you have multiple, given we strip the token. But maybe we need to be consistent across all 3?

I am a bit worried about silently ignoring the auth though....

Copy link
Contributor Author

@jmcguire98 jmcguire98 Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's definitely a fair point.

I'm okay with changing the MCP authn behavior to reject in cases where the token was stripped earlier on if we think multiple JWT policy configuration is a complete anti-pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just made the update to follow the behavior of the first 2 for the time being. We can always come back and address the other cases as a follow up later if there is demand.

@npolshakova
Copy link
Collaborator

Confirmed it works with kgateway xds + keycloak! 🎉 kgateway-dev/kgateway#12928

@npolshakova npolshakova marked this pull request as ready for review November 20, 2025 15:08
@npolshakova npolshakova requested a review from a team as a code owner November 20, 2025 15:08
Copilot AI review requested due to automatic review settings November 20, 2025 15:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for MCP authentication when configured via xDS (dynamic configuration). Previously, MCP authentication only worked with local configuration using route-level JWT policies. The new implementation performs JWT validation directly in the MCP backend policy, allowing the control plane to provide JWKS inline rather than requiring dynamic JWKS fetching.

Key changes:

  • Changed audience (singular) to audiences (array) and jwksUrl to jwks (supporting inline/file/URL) in the MCP authentication schema
  • Added inline JWT validation in the MCP router for xDS-configured backends, bypassing the normal backend policy flow
  • Modified the proto definition to use jwks_inline and audiences fields, with resource metadata now using map<string, string> instead of map<string, google.protobuf.Value>

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
schema/local.json Updated JSON schema to support audiences array and jwks object with file/url/inline options
schema/README.md Updated documentation to reflect the new schema field names
go/api/resource.pb.go Generated protobuf code reflecting proto changes to audiences and jwks_inline fields
examples/mcp-authentication/config.yaml Migrated example configs from audience/jwksUrl to audiences/jwks.url
crates/agentgateway/src/types/agent_xds.rs Added xDS conversion logic to parse inline JWKS and create JWT validators
crates/agentgateway/src/types/agent.rs Updated McpAuthentication struct with audiences array, jwks field, and jwt_validator
crates/agentgateway/src/mcp/router.rs Implemented JWT validation logic directly in router for MCP backends
crates/agentgateway/proto/resource.proto Changed proto fields from audience/jwks_url to audiences/jwks_inline and simplified resource metadata

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let val = serde_json::from_str::<serde_json::Value>(v)
.unwrap_or(serde_json::Value::String(v.clone()));
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from serde_json::from_str is silently ignored. Consider logging the parse error to help diagnose configuration issues when the control plane provides malformed JSON values in resource metadata.

Copilot uses AI. Check for mistakes.
Signed-off-by: npolshakova <nina.polshakova@solo.io>
Signed-off-by: npolshakova <nina.polshakova@solo.io>
Signed-off-by: npolshakova <nina.polshakova@solo.io>
@npolshakova npolshakova enabled auto-merge (squash) November 24, 2025 20:30
@howardjohn howardjohn disabled auto-merge November 24, 2025 22:15
@howardjohn howardjohn merged commit d2f2056 into agentgateway:main Nov 24, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants