For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Auth0
Protect MCP servers with Auth0 as the authorization server.
Auth0 is an identity platform that provides authentication and authorization services. Agentgateway includes a native auth0 MCP authentication provider so that you can use Auth0 as the authorization server for your MCP servers.
In this guide, you create an API and applications in Auth0, protect a sample MCP server with the auth0 provider, and verify that agentgateway rejects unauthenticated requests and admits tokens that Auth0 issues.
Why the Auth0 provider is needed
MCP clients follow the MCP authorization specification, which relies on OAuth behaviors that Auth0 implements differently. Auth0 does not support RFC 8707 resource indicators (auth0#66169), which MCP clients use to request a token for a specific resource. Instead, Auth0 expects its own audience parameter. Without a workaround, Auth0 issues an opaque access token that agentgateway cannot validate as a JWT.
When you set provider.auth0, agentgateway bridges this gap as follows:
- Appends your first configured audience to Auth0’s authorization endpoint as an
audiencequery parameter, so that Auth0 issues a JWT for your API rather than an opaque token. You verify this in Step 3. - Fetches keys from
{issuer}/.well-known/jwks.json, which is where Auth0 publishes them.
For the underlying mcpAuthentication fields, see MCP authentication.
Before you begin
- Install the agentgateway binary.
- Install Node.js so that
npxcan run the sample MCP server. - Make sure that you have access to an Auth0 tenant and permission to create an API and applications in it. A free tenant is sufficient.
Step 1: Set up Auth0
Create an API and the applications that your clients use, then collect the values that agentgateway needs.
In the Auth0 Dashboard, go to Applications > APIs and click Create API. Enter a name such as
agentgateway API, and set the Identifier to the resource URL that your MCP clients request, such ashttps://api.example.com. The identifier becomes theaudclaim of the tokens that Auth0 issues.On the API’s Settings tab, enable Add Permissions in the Access Token. Then define the permissions that your MCP server enforces on the Permissions tab, such as
read:tools. You use this permission in Step 5.Go to Applications > Applications and click Create Application. Choose Native 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. On the application’s Settings tab, under Application URIs, add the callback URLs of the MCP clients that you plan to connect.
Create a second application of type Machine to Machine, authorize it for the API that you created, and grant it the
read:toolspermission. You use this application to request a token from the command line in Step 4, which keeps the verification steps scriptable. Note its Client ID and Client Secret.Save the values that the rest of this guide uses.
export AUTH0_TENANT_URL='https://your-tenant.us.auth0.com' export AUTH0_ISSUER="${AUTH0_TENANT_URL}/" export AUTH0_AUDIENCE='https://api.example.com' export AUTH0_CLIENT_ID='<your-m2m-client-id>' export AUTH0_CLIENT_SECRET='<your-m2m-client-secret>'Variable Where to find it AUTH0_TENANT_URLYour tenant URL, including https://and with no trailing slash. The Domain on any application’s Settings tab, prefixed withhttps://.AUTH0_ISSUERDerived from the tenant URL. Auth0 mints the issclaim with a trailing slash, so this value keeps it.AUTH0_AUDIENCEThe Identifier of the API that you created in step 1. AUTH0_CLIENT_IDandAUTH0_CLIENT_SECRETThe Settings tab of the machine-to-machine application from step 4. Agentgateway expands
${...}references when it loads a configuration file, so the same variables also fill in theconfig.yamlthat you create next. If a variable is unset, agentgateway exits withenvironment variable not foundrather than starting with a broken configuration.
Step 2: Configure and start agentgateway
Create a
config.yamlfile that exposes a sample MCP server on port 3000 and protects it with theauth0provider.Review the following table to understand this configuration.# yaml-language-server: $schema=https://agentgateway.dev/schema/config gateways: default: port: 3000 routes: - backends: - mcp: targets: - name: everything stdio: cmd: npx args: ["@modelcontextprotocol/server-everything"] policies: mcpAuthentication: mode: strict issuer: ${AUTH0_ISSUER} audiences: - ${AUTH0_AUDIENCE} provider: auth0: {} resourceMetadata: resource: http://localhost:3000/mcp scopesSupported: - read:tools bearerMethodsSupported: - headerSetting Description issuerYour Auth0 tenant domain, including the trailing slash. Auth0 mints the issclaim with a trailing slash, and this value must match it.audiencesThe Identifier of your Auth0 API. The first entry is the value that agentgateway sends to Auth0 as the audiencequery parameter, so list your API identifier first.provider.auth0Enables the Auth0-specific behavior described in Why the Auth0 provider is needed. Takes no fields. resourceMetadataThe protected resource metadata that agentgateway serves to MCP clients, which you inspect in Step 3. jwksOptional. Because provider.auth0is set, agentgateway derives the JWKS URL from the issuer. To fetch keys from somewhere else, such as a local file or an internal mirror, setjwksexplicitly to override the derived URL.Start agentgateway.
agentgateway -f config.yamlExample output:
info state_manager loaded config from File("config.yaml") info app serving UI at http://localhost:15000/ui info proxy::gateway started bind bind="bind/3000"
Step 3: Verify that unauthenticated requests are rejected
Agentgateway runs in the foreground, so run the following commands in another terminal.
Send an MCP
initializerequest without a token.curl -i -X POST http://localhost:3000/mcp \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}},"id":1}'Agentgateway returns
401with aWWW-Authenticateheader that points MCP clients at the protected resource metadata.HTTP/1.1 401 Unauthorized www-authenticate: Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource/mcp" {"error":"unauthorized","error_description":"JWT token required"}Follow that pointer to see the metadata that the gateway serves.
curl -s http://localhost:3000/.well-known/oauth-protected-resource/mcpExample output:
{"resource":"http://localhost:3000/mcp","authorization_servers":["http://localhost:3000/mcp"],"mcp_protocol_version":"2025-06-18","resource_type":"mcp-server","bearer_methods_supported":["header"],"scopes_supported":["read:tools"]}Confirm that agentgateway appends your audience to Auth0’s authorization endpoint.
curl -s http://localhost:3000/.well-known/oauth-authorization-serverThe
authorization_endpointcarries anaudiencequery parameter that Auth0’s own discovery document does not include.... "authorization_endpoint": "https://your-tenant.us.auth0.com/authorize?audience=https://api.example.com", "token_endpoint": "https://your-tenant.us.auth0.com/oauth/token",
Step 4: Call the MCP server with a token
MCP clients complete the OAuth flow themselves. To get a token by hand, use the client credentials flow with the machine-to-machine application from Step 1.
Request a token from your tenant. The
audienceparameter is what makes Auth0 return a JWT for your API rather than an opaque token.export TOKEN="$(curl -s -X POST "${AUTH0_TENANT_URL}/oauth/token" \ -d grant_type=client_credentials \ -d "client_id=${AUTH0_CLIENT_ID}" \ -d "client_secret=${AUTH0_CLIENT_SECRET}" \ -d "audience=${AUTH0_AUDIENCE}" \ | jq -r .access_token)"Send the token as a bearer token.
curl -i -X POST http://localhost:3000/mcp \ -H "authorization: Bearer ${TOKEN}" \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}},"id":1}'Agentgateway fetches Auth0’s keys from the derived JWKS URL, validates the token, and returns the MCP server’s response.
HTTP/1.1 200 OK content-type: text/event-stream mcp-session-id: 0511047b-3f97-4dcf-9fec-4457b4c3c229 event: message data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05", ... ,"serverInfo":{"name":"mcp-servers/everything","title":"Everything Reference Server","version":"2.0.0"}}}Tip
The client credentials flow is a convenience for this guide. Real MCP clients use the authorization code flow with PKCE, which the gateway advertises through the metadata that you inspected in Step 3.
Step 5: Restrict access by permission
Because MCP authentication runs at the route level, you can use claims from the validated Auth0 token in an authorization policy. Auth0 puts the permissions that you grant to an application in the permissions claim when the API has Add Permissions in the Access Token enabled, which you did in Step 1.
Add an
authorizationpolicy alongsidemcpAuthenticationin yourconfig.yamlthat requires theread:toolspermission.policies: mcpAuthentication: mode: strict issuer: ${AUTH0_ISSUER} audiences: - ${AUTH0_AUDIENCE} provider: auth0: {} resourceMetadata: resource: http://localhost:3000/mcp scopesSupported: - read:tools bearerMethodsSupported: - header authorization: rules: # Check for the read:tools permission in the token - '"read:tools" in jwt.permissions'Restart agentgateway to apply the policy. Because the machine-to-machine application was granted
read:tools, the request from Step 4 still succeeds.agentgateway -f config.yamlTo confirm that the rule is enforced, create another Machine to Machine application in Auth0, authorize it for your API, but do not grant it the
read:toolspermission. Save its credentials.export AUTH0_UNAUTHORIZED_CLIENT_ID='<second-m2m-client-id>' export AUTH0_UNAUTHORIZED_CLIENT_SECRET='<second-m2m-client-secret>'Request a token with that application and repeat the request.
export NO_PERM_TOKEN="$(curl -s -X POST "${AUTH0_TENANT_URL}/oauth/token" \ -d grant_type=client_credentials \ -d "client_id=${AUTH0_UNAUTHORIZED_CLIENT_ID}" \ -d "client_secret=${AUTH0_UNAUTHORIZED_CLIENT_SECRET}" \ -d "audience=${AUTH0_AUDIENCE}" \ | jq -r .access_token)" curl -s -o /dev/null -w '%{http_code}\n' -X POST http://localhost:3000/mcp \ -H "authorization: Bearer ${NO_PERM_TOKEN}" \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}},"id":1}'The token is valid, so authentication succeeds, but the authorization rule denies the request with
403.403
Connect an MCP client
Point your MCP client at the gateway’s MCP endpoint, http://localhost:3000/mcp. The client discovers the authorization server through the gateway, and redirects the user to Auth0 to log in and consent.