quickoauth
← Guides

Add OAuth to your MCP server with Auth0 (2026, no proxy)

A start-to-finish Auth0 setup for MCP connectors, verified live: free signup, dynamic client registration, native RFC 8707 resource support, and a Worker that validates Auth0 JWTs offline. Every step was executed for real and screenshotted.

auth0 · updated 2026-08-22


We created a fresh Auth0 account, configured the tenant through the Management API, deployed a test MCP resource server on Cloudflare Workers, and ran a real token through the whole chain. The steps below are what that took. The testbed it produced runs at https://qo-test-auth0.rough-disk-9b56.workers.dev/mcp and our RL suite checks it on every deploy.

Auth0 in 2026 no longer needs the audience-proxy workaround from our older Auth0 proxy guide. New tenants ship with resource_parameter_profile: "audience", which maps the MCP client’s RFC 8707 resource parameter onto Auth0’s audience concept, and OIDC dynamic client registration is one tenant flag away. Point your protected resource metadata straight at the tenant.

1. Sign up (free, no credit card)

auth0.com/signup: email, a 6-digit code sent to your inbox, a password, done. The free plan includes everything this guide uses; the trial banner you’ll see only concerns paid features.

Auth0 signup form

Email verification code step

You get a dev tenant like dev-xxxxxxxx.us.auth0.com.

Fresh Auth0 dashboard

2. Get a Management API token

Dashboard → APIs → Auth0 Management APIAPI Explorer tab → “Create & Authorize Test Application”. Copy the token (24 h lifetime, all scopes). For production automation, mint scoped tokens via the client-credentials exchange instead.

API Explorer token

3. Configure the tenant via the Management API

Three PATCH/POST calls do all the work. $TOK is the Management token, $D your tenant domain, $AUD the exact URL of your MCP endpoint.

# enable OIDC dynamic client registration (what Claude/ChatGPT/Grok use)
curl -X PATCH "https://$D/api/v2/tenants/settings" \
  -H "Authorization: Bearer $TOK" -H 'content-type: application/json' \
  -d '{"flags":{"enable_dynamic_client_registration":true}}'

# native MCP support: resource parameter → audience, plus CIMD for ChatGPT
curl -X PATCH "https://$D/api/v2/tenants/settings" \
  -H "Authorization: Bearer $TOK" -H 'content-type: application/json' \
  -d '{"resource_parameter_profile":"audience","client_id_metadata_document_supported":true}'

# register your MCP server as an API (identifier = audience = the MCP URL)
curl -X POST "https://$D/api/v2/resource-servers" \
  -H "Authorization: Bearer $TOK" -H 'content-type: application/json' \
  -d '{"name":"my MCP","identifier":"'$AUD'","signing_alg":"RS256","allow_offline_access":true}'

Two more things DCR-registered (third-party) clients need:

# promote the database connection to domain level so dynamic clients can log users in
curl -X PATCH "https://$D/api/v2/connections/{connection_id}" \
  -H "Authorization: Bearer $TOK" -H 'content-type: application/json' \
  -d '{"is_domain_connection":true}'

Watch out: patching one tenant setting can reset resource_parameter_profile (ours flipped from audience to compatibility after an unrelated PATCH). Set it explicitly and re-read the settings afterwards.

4. Verify discovery and DCR before touching a browser

curl https://$D/.well-known/oauth-authorization-server
# issuer, /oidc/register as registration_endpoint, S256 all present

curl -X POST https://$D/oidc/register -H 'content-type: application/json' -d '{
  "client_name": "probe",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}'

What we hit doing this for real:

  • The DCR response contains no null or empty-string values (ChatGPT’s strict schema is satisfied without a scrub proxy, unlike Hydra).
  • Registered clients get tpc_… client IDs and a THIRD-PARTY badge in the dashboard.

DCR-registered third-party client in the Applications list

  • A client_secret is returned even with token_endpoint_auth_method: "none" (public clients just never use it).
  • There is no RFC 7592 management URI in the response: DCR clients accumulate in the tenant and must be pruned via the Management API.
  • The metadata issuer and every token’s iss claim carry a trailing slash (https://dev-….us.auth0.com/). Compare issuers exactly against that form in your resource server, but advertise the origin (no slash) in your protected-resource metadata’s authorization_servers.

5. The resource server: 401 challenge + offline JWT validation

Your MCP server needs three behaviours: serve /.well-known/oauth-protected-resource, answer unauthenticated requests with 401 + a WWW-Authenticate challenge naming that metadata URL, and validate Bearer JWTs against the tenant’s JWKS (https://$D/.well-known/jwks.json, RS256) checking iss (with the slash), aud (your MCP URL, delivered thanks to the audience profile), and exp. Our reference implementation is ~150 dependency-free lines of Worker code, RL/testbeds/lib/resource-worker.mjs in the quickoauth repo, running live at the testbed URL above.

6. Prove the chain headlessly

For CI you don’t want a browser in the loop. Enable the password grant on a test client, set the tenant’s default_directory to your database connection, create a test user, and:

curl -X POST https://$D/oauth/token \
  -d grant_type=password -d "username=$TEST_USER" -d "password=$TEST_PASS" \
  -d "audience=$AUD" -d "client_id=$CID" -d "client_secret=$CSEC"

The returned access_token is a real RS256 user JWT with aud = your MCP URL. Send it to your /mcp endpoint; a garbage token must get 401 back. That exact sequence (discovery, DCR, token mint, authenticated MCP call, negative test) is what our RL suite replays against this tenant on every deploy.

Next steps

Finish the setup against your own tenant:

  1. Run the four-curl self-test from the connector checklist against your MCP domain: 401 probe, both metadata documents, DCR.
  2. Add the connector in claude.ai under Customize → Connectors and complete a real login.
  3. Extend the host allowlist and re-test for ChatGPT and Grok with one server, three connectors.

To hand the steps to an agent instead, copy the Auth0 native MCP prompt (auth0-mcp-native). On an older tenant without the audience profile, the thin-proxy architecture still applies; if the connector dialog fails, work through debugging connector failures. The same walkthrough exists for Stytch and Supabase.

Metadata

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