quickoauth
← Guides

Auth0 in front of an MCP server, with a thin proxy

The architecture that got a self-hosted MCP server through claude.ai connector validation: Auth0 for login and tokens, a small proxy for the metadata and DCR endpoints Auth0 does not serve the way MCP clients expect.

auth0 · updated 2026-08-22


Auth0 gives you login, token issuance, and refresh out of the box — but an MCP client discovers your OAuth setup through metadata endpoints on your domain, and registers itself via dynamic client registration. Pointing claude.ai straight at an Auth0 tenant fails on both counts. The fix is a thin proxy on your MCP domain that owns discovery and DCR, and delegates the actual authorization to Auth0.

The shape

claude.ai

  ├─ /.well-known/oauth-protected-resource   ← proxy (static JSON)
  ├─ /.well-known/oauth-authorization-server ← proxy (static JSON, endpoints on your domain)
  ├─ /register                               ← proxy (DCR: issue/echo a client_id)
  ├─ /authorize ──────────────► Auth0 /authorize (redirect, params mapped)
  ├─ /token ──────────────────► Auth0 /token    (server-to-server exchange)

  └─ /mcp  ── Bearer token ──► your MCP server (validates Auth0 JWT)

Everything the client sees lives on one domain. Auth0 only appears in the middle of the browser redirect, where users expect a login page.

Auth0 tenant setup

  1. Create a Regular Web Application. Note the client ID and secret — the proxy uses these when it forwards /token.
  2. Create an API in Auth0 (this becomes the JWT audience), identifier set to your MCP URL, e.g. https://yourdomain.com/mcp.
  3. Allowed callback URLs: your proxy’s /callback if you terminate the redirect yourself, or the claude.ai callbacks directly if you pass them through: https://claude.ai/api/mcp/auth_callback, https://claude.com/api/mcp/auth_callback.

The proxy endpoints

Metadata — two static JSON routes. The authorization-server document advertises your /authorize, /token, /register; S256 in code_challenge_methods_supported.

/register (DCR) — Auth0’s open DCR is off by default and issues clients without your API’s grants. Simplest reliable approach: don’t call Auth0 at all. Accept the registration, store the redirect_uris, and return your existing Auth0 application’s client_id. Every MCP client shares one Auth0 app; per-client isolation comes from the tokens, not the client record.

/authorize — 302 to https://TENANT.auth0.com/authorize?audience=https://yourdomain.com/mcp&… forwarding client_id (mapped to the real Auth0 one), redirect_uri, state, code_challenge, code_challenge_method, scope.

/token — forward the code exchange to Auth0’s /token, adding the client secret. Return Auth0’s JSON unchanged. Refresh grants pass through the same way.

Token validation at the MCP server

Validate the incoming Authorization: Bearer JWT against your tenant’s JWKS (https://TENANT.auth0.com/.well-known/jwks.json): signature, iss, exp, and — the one people skip — aud equal to your API identifier. Reject with 401 + WWW-Authenticate so clients know to re-authenticate.

Serving it

Any TLS-terminating reverse proxy works. A minimal Caddyfile:

yourdomain.com {
    handle /mcp* {
        reverse_proxy localhost:8000
    }
    handle /.well-known/* {
        reverse_proxy localhost:8001
    }
    handle /authorize* { reverse_proxy localhost:8001 }
    handle /token*     { reverse_proxy localhost:8001 }
    handle /register*  { reverse_proxy localhost:8001 }
}

Caddy provisions and renews the Let’s Encrypt certificate on its own. The only DNS requirement: the hostname resolves publicly — see the pre-flight section of the checklist.

Verify

Run the four-curl self-test from the connector checklist, then add the connector in claude.ai under Customize → Connectors. You should land on the Auth0 login page, and after consent the tools list should populate.

Next steps

Before building this proxy, check whether you still need it: 2026 Auth0 tenants support the resource parameter natively (resource_parameter_profile: "audience") and OIDC dynamic client registration, which removes the proxy entirely — the no-proxy Auth0 guide is the current recommendation and was verified live. If you’re staying on the proxy (older tenant, extra control at the edge):

  1. Run the four-curl self-test from the connector checklist.
  2. Connect in claude.ai, then extend to ChatGPT and Grok via one server, three connectors.
  3. On failures, step through debugging connector failures.

The proxy prompt (auth0-front-mcp) builds this architecture for you.

Metadata

Commit
8d09f70
Browser
Current Time
Dimensions
Source
guides
Last Updated
2026-08-22