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.


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

2. Get a Management API token
Dashboard → APIs → Auth0 Management API → API 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.

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.

- A
client_secretis returned even withtoken_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
issuerand every token’sissclaim 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’sauthorization_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:
- Run the four-curl self-test from the connector checklist against your MCP domain — 401 probe, both metadata documents, DCR.
- Add the connector in claude.ai under Customize → Connectors and complete a real login.
- 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
- b5cb085
- Browser
- —
- Current Time
- —
- Dimensions
- —
- Source
- guides
- Last Updated
- 2026-08-22