AI Gateway Enterprise: This plugin is only available as part of our AI Gateway Enterprise offering.
The AI MCP OAuth2 plugin secures Model Context Protocol (MCP) traffic on AI Gateway using OAuth 2.0 specification for MCP servers. It ensures only authorized MCP clients can access protected MCP servers, and acts as a crucial security layer for MCP servers.
Breaking change
v3.13+The MCP OAuth2 plugin now treats all incoming traffic as MCP requests to address a potential authentication bypass vulnerability.
Use MCP OAuth2 with MCP Proxy in listener or passthrough-listener modes. For REST API exposure, configure MCP Proxy in conversion-only mode on a separate route.
The plugin provides OAuth 2.0 authentication for MCP traffic, allowing MCP clients to safely request access. It validates that access tokens are issued specifically for the target MCP server, ensuring only authorized requests are accepted. To reduce the risk of token theft or confused deputy attacks, the plugin does not pass access tokens to upstream services.
The plugin performs three core functions:
Validates incoming MCP requests by verifying access tokens from an external Authorization Server.
Extracts claims from validated tokens and forwards them to upstream MCP services via headers.
Ensures compliance with MCP authorization requirements based on OAuth 2.1.
The plugin follows the following authorization flow:
AI Gateway acts as the Resource Server, enforcing access control.
The MCP clients send requests with a valid Authorization: Bearer <access-token> header.
The plugin validates tokens, checks the intended audience, and blocks invalid or expired tokens with a 401 Unauthorized.
Access tokens are not forwarded to upstream services by default, protecting against token theft or confused deputy attacks.
sequenceDiagram
participant C as MCP client
participant K as AI MCP OAuth2 plugin
participant AS as Authorization server
participant U as Upstream MCP server
C->>K: Discover protected resource metadata
activate K
K-->>C: Protected resource metadata (includes auth server address)
deactivate K
C->>AS: Request access token
activate AS
AS-->>C: Access token
deactivate AS
C->>K: MCP auth request
activate K
K->>AS: Introspect token
activate AS
AS-->>K: Valid / invalid
deactivate AS
alt If token valid
K->>U: Forward request with claims as headers
activate U
U-->>K: MCP server response
deactivate U
K-->>C: MCP response
else If token invalid
K-->>C: 401 Unauthorized
end
deactivate K
The AI MCP OAuth2 plugin is designed to secure MCP traffic as early as possible in the request lifecycle to prevent unauthorized access before any AI-specific processing occurs.
Note: Like, the AI MCP Proxy plugin, the AI MCP OAuth2 plugin is not invoked as part of an LLM request flow.
Instead, it is registered and executed as a regular plugin, allowing it to capture MCP traffic independently of LLM request flow.
The AI MCP OAuth2 plugin can be used on its own for upstream MCP proxying or in combination with the AI MCP Proxy plugin when request/response conversion is needed.
The plugin supports two token validation methods. When introspection is configured, it is always used. JWKS is only used when no introspection endpoint is configured.
Introspection: Set config.introspection_endpoint to have the plugin call the authorization server to validate opaque tokens. Requires config.client_id when config.client_auth is client_secret_basic or client_secret_post.
JWKS: Set config.jwks_endpoint to validate signed JWTs locally using the authorization server’s public keys. If not set, the plugin attempts to discover the JWKS URI from the authorization server metadata.
The plugin can extract claims from a validated token and forward them to the upstream MCP server as HTTP headers. Two approaches are available, and they are mutually exclusive.
The plugin can map token claims to Kong consumers and consumer groups, enabling consumer-based rate limiting, ACL, and other consumer-aware plugins to function with MCP traffic.
Set config.consumer_claim to the path of the claim to use for consumer lookup. If multiple strings are provided, the plugin treats them as a nested path in the token payload. For example, ["sub"] maps the top-level sub claim, while ["realm_access", "user_id"] maps token.realm_access.user_id.
Use config.consumer_by to control which consumer fields are checked during lookup. Accepted values are id, username, and custom_id. Defaults to ["username", "custom_id"].
Set config.consumer_optional to true if you want the plugin to continue without failing when no matching consumer is found.
Set config.consumer_groups_claim to the path of the claim containing the consumer group names. If multiple strings are provided, the plugin treats them as a nested path.
When consumer mapping is not used, set config.credential_claim to derive a virtual credential from the token. This credential is used by plugins like rate-limiting to track usage. Defaults to ["sub"].
Token exchange lets the plugin swap the client’s access token for a different token before forwarding the request to the upstream MCP server. This is useful when the upstream requires a token from a different authorization server or with different scopes.
When config.token_exchange.enabled is true, the plugin performs the following after validating the incoming token:
sequenceDiagram
participant C as MCP client
participant K as AI MCP OAuth2 plugin
participant AS as Authorization server
participant TE as Token exchange endpoint
participant U as Upstream MCP server
C->>K: MCP request with Bearer token
activate K
K->>AS: Validate token (introspect / JWKS)
activate AS
AS-->>K: Token valid
deactivate AS
K->>TE: Token exchange request (subject_token = original token)
activate TE
TE-->>K: Exchanged access token
deactivate TE
K->>U: Forward request with exchanged token
activate U
U-->>K: MCP server response
deactivate U
K-->>C: MCP response
deactivate K
The client_auth field controls how the plugin authenticates with the token exchange endpoint. Accepted values are client_secret_basic, client_secret_post, none, and inherit. When inherit is used, the plugin reuses the client_id and client_secret configured for the introspection endpoint.
When config.token_exchange.request.actor_token_source is set to header, provide the name of the header carrying the actor token in actor_token_header. When set to config, provide the static actor token value in actor_token.
Exchanged tokens are cached by default. Set config.token_exchange.cache.enabled to false to disable caching. The TTL defaults to 3600 seconds and is used when the token exchange endpoint does not return an expires_in value.
By default, the plugin strips the incoming access token before forwarding the request to the upstream MCP server, preventing token theft and confused deputy attacks. Set config.passthrough_credentials to true to keep the original token in the request.
Only enable token passthrough when the upstream MCP server explicitly requires the original access token, or when token exchange is configured.