For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Set up Okta
Configure Okta as an OAuth identity provider for MCP authentication with agentgateway.
Secure your Model Context Protocol (MCP) servers with OAuth 2.0 authentication by using agentgateway and Okta as the identity provider.
About this guide
In this guide, you configure the agentgateway proxy to protect a static MCP server with MCP auth by using Okta as the authorization server. Because Okta does not fully implement the OAuth behaviors that the MCP authorization specification assumes, agentgateway includes a native Okta provider that bridges the gaps. When you set provider: Okta, agentgateway does the following:
- Serves authorization server metadata from Okta’s OpenID Connect discovery document, because Okta does not support the RFC 8414 path-based issuer format.
- Appends your first configured audience to Okta’s authorization endpoint as an
audiencequery parameter, because Okta does not support RFC 8707 resource indicators. - Proxies Dynamic Client Registration through the gateway, because Okta does not send CORS headers on its registration endpoint. Okta’s registration endpoint is relative to your org URL rather than the issuer, so agentgateway rewrites it to
https://<your-org>.okta.com/oauth2/v1/clients.
Important
Set the JWKS path to /oauth2/<auth-server-id>/v1/keys. Okta publishes its signing keys there, not at the /.well-known/jwks.json path that many other identity providers use. Pointing jwksPath at the wrong path means the control plane cannot fetch Okta’s keys, and token validation fails.
For more information about MCP auth, see the About MCP auth page.
Before you begin
- Set up an agentgateway proxy.
- Follow the steps to set up an MCP server with a fetch tool.
- Install the experimental channel Gateway API.
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/experimental-install.yaml
Set up Okta
Create an app integration in Okta, and collect the values that agentgateway needs.
Make sure that you have access to an Okta org. If you do not have one, you can create a free developer org.
In the Okta Admin Console, go to Applications > Applications and click Create App Integration. Select OIDC - OpenID Connect as the sign-in method, and select Native Application for local MCP clients or Single-Page Application for browser-based clients. Both are public clients that use PKCE, which is what MCP clients require.
Under Grant type, select Authorization Code and Refresh Token. Under Sign-in redirect URIs, add the callback URLs of the MCP clients that you plan to connect. Assign the app to the users or groups that need access, then click Save.
On the app’s General tab, note the Client ID.
Go to Security > API > Authorization Servers. Use the
defaultauthorization server, or add one for your MCP server. Note the Audience value on the server’s Settings tab, and add a scope on the Scopes tab if your MCP server enforces scopes.On the authorization server’s Claims tab, add a
groupsclaim so that a user’s group memberships appear in the access token. Then, go to Directory > Groups, create a group such asAI-Users, and add the users that you want to access the MCP server. You use this group in the authorization rule that you configure later.Save the values as environment variables.
export OKTA_DOMAIN=<your-org>.okta.com export OKTA_AUTH_SERVER=default export OKTA_CLIENT_ID=<your-app-client-id> export OKTA_AUDIENCE=api://defaultVariable Description OKTA_DOMAINYour Okta org domain, without a scheme or trailing slash, such as dev-1234567.okta.com.OKTA_AUTH_SERVERThe ID of the authorization server to use. The built-in server is default.OKTA_CLIENT_IDThe Client ID of the app integration that you created. OKTA_AUDIENCEThe Audience of the authorization server. For the defaultserver, this value isapi://default. Okta sets theaudclaim of its access tokens to this value.Tip
To confirm the issuer and JWKS path for your authorization server, open its metadata document at
https://<your-org>.okta.com/oauth2/<auth-server-id>/.well-known/openid-configurationand check theissuerandjwks_urifields.
Create the JWKS backend
Create an AgentgatewayBackend that points to your Okta org, and a BackendTLSPolicy that originates a TLS connection to it. The JWT authentication policy uses this backend to fetch Okta’s public keys for token signature validation.
Create an AgentgatewayBackend for your Okta org.
kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: name: okta-jwks spec: static: host: ${OKTA_DOMAIN} port: 443 EOFCreate a BackendTLSPolicy that originates a TLS connection to the
okta-jwksbackend by using well-known trusted CA certificates.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: BackendTLSPolicy metadata: name: okta-jwks spec: targetRefs: - name: okta-jwks kind: AgentgatewayBackend group: agentgateway.dev validation: hostname: ${OKTA_DOMAIN} wellKnownCACertificates: System EOF
Configure MCP auth
With your MCP backend configured, create an AgentgatewayPolicy that enforces Okta authentication and authorization for the MCP backend.
Create an AgentgatewayPolicy with the
Oktaprovider. The policy validates tokens that Okta issues and uses a Common Expression Language (CEL) rule to require theAI-Usersgroup.kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayPolicy metadata: name: mcp-okta-authn spec: # Target the HTTPRoute to apply authentication at the route level targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: mcp traffic: jwtAuthentication: mode: Strict providers: # The issuer is the authorization server URL, with no trailing slash - issuer: "https://${OKTA_DOMAIN}/oauth2/${OKTA_AUTH_SERVER}" audiences: - "${OKTA_AUDIENCE}" jwks: remote: backendRef: name: okta-jwks kind: AgentgatewayBackend group: agentgateway.dev port: 443 # Okta serves keys at {issuer}/v1/keys, not /.well-known/jwks.json jwksPath: "/oauth2/${OKTA_AUTH_SERVER}/v1/keys" mcp: # Use the native Okta provider to bridge Okta's OAuth behaviors provider: Okta # Short-circuit Dynamic Client Registration with a pre-registered client clientId: "${OKTA_CLIENT_ID}" resourceMetadata: resource: http://localhost:8080/mcp scopesSupported: - openid - profile bearerMethodsSupported: - header # Allow only tokens from members of the AI-Users group authorization: action: Allow policy: matchExpressions: - '"AI-Users" in jwt.groups' EOFReview the following table to understand this configuration. For more information about the
traffic.jwtAuthenticationfield, see the API docs.Setting Description providers[].issuerThe Okta authorization server URL, such as https://dev-1234567.okta.com/oauth2/default. This value must match theissclaim in the token.providers[].audiencesThe Audience of your Okta authorization server. This value must match the audclaim in the token. Agentgateway also sends the first audience to Okta as theaudiencequery parameter.providers[].jwks.remote.backendRefThe okta-jwksbackend that points to your Okta org.providers[].jwks.remote.jwksPathThe path to Okta’s JWKS endpoint. Okta serves keys at /oauth2/<auth-server-id>/v1/keys.mcp.providerThe identity provider. Set to Oktato enable the native Okta bridging behavior.mcp.clientIdThe Client ID of your Okta app integration. Agentgateway answers Dynamic Client Registration requests with this value instead of proxying them to Okta. mcp.resourceMetadataMCP OAuth resource metadata for discovery. Includes the resource identifier, supported scopes, and bearer token methods. authorization.policy.matchExpressionsCEL rules that authorize the claims in the verified JWT. This example requires membership in the AI-UsersOkta group. Requests that present a valid token without that group are denied with a 403 HTTP response code.Note
Setting
clientIdis recommended for Okta. Okta’s Dynamic Client Registration endpoint usually requires an API token that MCP clients do not have, so registration requests that the gateway proxies to Okta fail. A pre-registered client avoids that dependency.Verify that the policy was accepted.
kubectl get AgentgatewayPolicy mcp-okta-authn -o yamlIn the
statussection, confirm that theAcceptedandAttachedconditions areTrue.Note
The control plane fetches the JWKS when it translates the policy. If your Okta domain or JWKS path is wrong, the policy is accepted but the control plane logs
jwks keyset ... isn't availableand the policy does not program on the data plane. Check the control plane logs if authentication does not take effect.Update the HTTPRoute that routes incoming traffic to the MCP server to include the OAuth discovery paths. This way, the agentgateway proxy can serve the resource and authorization server metadata during the MCP auth flow.
kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: mcp spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: agentgateway-proxy namespace: agentgateway-system rules: - filters: # Enable CORS for browser-based MCP clients - type: CORS cors: allowCredentials: true allowHeaders: - Origin - Authorization - Content-Type allowMethods: - "*" allowOrigins: - "*" exposeHeaders: - Origin - Mcp-Session-Id maxAge: 86400 backendRefs: - group: agentgateway.dev kind: AgentgatewayBackend name: mcp-backend matches: # Main MCP endpoint to connect to the MCP server - path: type: PathPrefix value: /mcp # Path to access resource server metadata - path: type: PathPrefix value: /.well-known/oauth-protected-resource/mcp # Path to access authorization server metadata, including the # gateway-proxied client registration endpoint - path: type: PathPrefix value: /.well-known/oauth-authorization-server/mcp EOF
Verify MCP auth
Get the address of the agentgateway proxy.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy \ -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo "Gateway address: $INGRESS_GW_ADDRESS"Send an unauthenticated request to the MCP endpoint. Verify that the request is rejected with a 401 HTTP response code and a
WWW-Authenticateheader that points MCP clients to the protected resource metadata.curl -i http://$INGRESS_GW_ADDRESS:80/mcp -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}},"id":1}'Example output:
HTTP/1.1 401 Unauthorized www-authenticate: Bearer resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource/mcp"Verify that the gateway serves the protected resource metadata.
curl -s http://$INGRESS_GW_ADDRESS:80/.well-known/oauth-protected-resource/mcp | jqVerify that the gateway serves Okta’s authorization server metadata, that the
audiencequery parameter is appended to the authorization endpoint, and that the registration endpoint points back at the gateway.curl -s http://$INGRESS_GW_ADDRESS:80/.well-known/oauth-authorization-server/mcp \ | jq '{issuer, jwks_uri, authorization_endpoint, registration_endpoint}'Example output:
{ "issuer": "https://dev-1234567.okta.com/oauth2/default", "jwks_uri": "https://dev-1234567.okta.com/oauth2/default/v1/keys", "authorization_endpoint": "https://dev-1234567.okta.com/oauth2/default/v1/authorize?audience=api://default", "registration_endpoint": "http://localhost:8080/.well-known/oauth-authorization-server/mcp/client-registration" }
Connect an MCP client
Point your MCP client at the gateway’s MCP endpoint, such as http://localhost:8080/mcp. The client discovers the authorization server through the gateway, registers against the pre-registered client, and redirects the user to Okta to log in and consent.
Group-based authorization
The policy that you created gates the MCP endpoint on the AI-Users group, which Okta puts in the groups claim of the access token when you add the groups claim to your authorization server. Authentication alone is not enough: any caller that Okta issues a token to for your audience passes JWT validation, including tokens that a client obtains for itself rather than for a user. The authorization rule denies those tokens with a 403 HTTP response code.
Because MCP authentication runs at the route level, every claim in the verified token is also available to other route-level policies, such as rate limiting and transformations. For more information about the rules that you can write, see Authorization.
To authorize individual tools instead of the whole MCP endpoint, use an MCP authorization policy. For more information, see Tool access.
Clean up
You can remove the resources that you created in this guide.kubectl delete AgentgatewayPolicy mcp-okta-authn
kubectl delete backendtlspolicy okta-jwks
kubectl delete AgentgatewayBackend okta-jwks