Configuring Auth0
The MCP Gateway can use Auth0 as the identity provider behind its downstream
OAuth flow. The mcp-auth0-oauth-inbound policy is an Auth0-friendly wrapper
around the generic mcp-oauth-inbound policy: provide your Auth0 domain, a
client ID, and a client secret, and the policy derives the OIDC issuer, JWKS
URL, and Auth0 authorize and token URLs for you.
This guide walks through the Auth0 dashboard setup, then shows how to wire the policy into your gateway project. Read the authentication overview first for the two-layer model and the role each policy plays.
This guide assumes you have a working Auth0 tenant. The Auth0 MCP client registration guide is the authoritative source for Auth0-side configuration.
How the wrapper derives configuration
Given a single auth0Domain like my-tenant.us.auth0.com, the wrapper derives
every URL the generic policy needs:
| Underlying option | Derived value |
|---|---|
oidc.issuer | https://my-tenant.us.auth0.com/ |
oidc.jwksUrl | https://my-tenant.us.auth0.com/.well-known/jwks.json |
browserLogin.url | https://my-tenant.us.auth0.com/authorize |
browserLogin.tokenUrl | https://my-tenant.us.auth0.com/oauth/token |
That's why most projects need only three fields: auth0Domain, clientId, and
clientSecret. The audience, scope, and TTL options are all optional
overrides.
auth0Domain is a bare hostname, not a URL. The policy rejects values that
include http:// or https:// prefixes or that don't contain a dot. Use
my-tenant.us.auth0.com, not https://my-tenant.us.auth0.com/.
Set up the Auth0 tenant
The MCP Gateway acts as an OAuth 2.1 authorization server in front of Auth0. Auth0 handles browser login and identity; the gateway issues its own access tokens that bind to MCP routes. The Auth0 application you create represents the gateway's identity against Auth0, not the MCP client.
Create an Auth0 application
- In the Auth0 Dashboard, open Applications > Applications and click Create Application.
- Set a name (for example,
Zuplo MCP Gateway). - Choose Regular Web Application as the application type and click Create.
- On the Settings tab, note the Domain, Client ID, and Client Secret. You'll wire these into the policy in the next section.
Configure callback and origin URLs
The gateway completes browser login by redirecting back to its own
/oauth/callback endpoint, so Auth0 needs that URL on its allow-list.
On the same Settings tab:
- Set Allowed Callback URLs to your gateway's
https://<gateway-host>/oauth/callback. For local development againstzuplo dev, addhttp://localhost:9000/oauth/callbackas well. - Set Allowed Web Origins to the gateway origin
https://<gateway-host>(plushttp://localhost:9000for local dev). - Save changes.
Optional: Set an audience
If you want Auth0 to issue identity-bound API access tokens (for example, to
validate Auth0-issued tokens against a specific resource server), create an API
under Applications > APIs with an identifier like
https://gateway.example.com and pass that identifier as the audience option
on the policy. When omitted, Auth0 acts only as the browser identity layer and
the gateway alone owns the OAuth grant the MCP client receives.
Connections and dynamic client registration
The downstream OAuth flow only requires Auth0 to authenticate the user and return an ID token. CIMD and DCR on Auth0's side concern the upstream MCP server's trust of clients, not the gateway's trust of Auth0. If you also configure Auth0 itself as an upstream MCP authorization provider (rare), follow Auth0's own guide for enabling CIMD or enabling DCR.
Wire the policy into the gateway
Add the policy to config/policies.json:
Code
Set the three environment variables in your Zuplo project's environment
configuration. AUTH0_DOMAIN is the bare hostname; the secret values belong in
the project secret store.
Attach the policy to each MCP route in config/routes.oas.json:
Code
Finally, register the gateway plugin in modules/zuplo.runtime.ts so the
runtime registers the OAuth endpoints automatically:
Code
One MCP OAuth policy serves every MCP route in the project — there's no need to declare it more than once. Attaching the same policy by name to each route is the canonical pattern.
Full options reference
mcp-auth0-oauth-inbound has three required options and a few optional
overrides. The complete schema is documented on the policy reference page; the
fields you'll touch most often are:
| Option | Required | Default | Notes |
|---|---|---|---|
auth0Domain | yes | — | Bare hostname (my-tenant.us.auth0.com). No scheme, must contain a dot. |
clientId | yes | — | Auth0 application client ID. |
clientSecret | yes | — | Auth0 application client secret. Use $env(...) to source from a secret. |
audience | no | unset | Optional Auth0 API identifier. Sent as the ?audience= parameter to Auth0's /authorize. |
scope | no | openid profile email | OIDC scopes requested during browser login. |
gateway.accessTokenTtlSeconds | no | 900 | Gateway-issued access token lifetime. |
gateway.refreshTokenTtlSeconds | no | ~10 years (runtime) | Gateway-issued refresh token lifetime. |
gateway.cimdEnabled | no | true | Advertise CIMD support in AS metadata. |
browserLoginOverrides.sessionTtlSeconds | no | 28800 | Browser session cookie lifetime (8 hours). |
browserLoginOverrides.stateTtlSeconds | no | 900 | Browser-login state record lifetime. |
browserLoginOverrides.remoteTimeoutMs | no | 10000 | Outbound timeout to Auth0 (token exchange, JWKS fetch). |
Test the configuration
The fastest sanity check is to try connecting an MCP client:
- Open Claude Desktop, Cursor, Claude Code, or another OAuth-aware MCP client.
- Add a remote MCP server pointing at one of your
/mcp/{slug}routes on the gateway. - The client should redirect you to Auth0's login page. After login, the gateway's consent screen renders. Approve it.
- The client receives an access token and can call
tools/list.
If something fails partway through, walk the flow manually using the
manual OAuth testing guide — it exercises every
endpoint with curl so you can see the raw responses.
Common issues
- "Invalid Auth0 domain" at boot. The
auth0Domainvalue includes a scheme prefix or doesn't contain a dot. Usemy-tenant.us.auth0.com. - Browser login redirects but the callback fails. The
https://<gateway-host>/oauth/callbackURL isn't on the Allowed Callback URLs list for the Auth0 application. - Token endpoint returns
invalid_audience. The MCP client is reusing a token bound to a different route. Each gateway-issued token binds to oneoperationId; the client must obtain a separate token per route. - Issuer in AS metadata is wrong. The gateway derives its issuer from the
incoming request origin. Check that your custom domain or proxy forwards the
correct
HostorX-Forwarded-Hostheader. See Troubleshooting. - MCP client can't discover the AS. Confirm the
mcp-auth0-oauth-inboundpolicy is attached to the route inroutes.oas.jsonand that theMcpGatewayPluginis registered inmodules/zuplo.runtime.ts. The internal OAuth endpoints register only when both are present.
Related
- Authentication overview
mcp-auth0-oauth-inboundpolicy reference- Configuring Okta or any other OIDC IdP
- Per-user OAuth to upstream MCP servers