mcp-auth0-oidc
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-auth0-oidclist my todos"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Secure MCP: Deploying Protected Remote MCP Servers on Cloudflare Workers with Auth0
A hands-on tutorial for securing and deploying remote MCP (Model Context Protocol) servers using Cloudflare Workers and Auth0. It walks through two connected pieces — a JWT-protected REST API and an OAuth-protected remote MCP server — from local development to production deployment, and documents every real error you're likely to hit along the way.
What this is
The Model Context Protocol (MCP) lets AI clients (Claude, IDEs, custom agents) call tools exposed by a server. When that server is remote — reachable over HTTP instead of running as a local subprocess — it needs the same access controls as any other API: you don't want an anonymous caller invoking tools that touch real user data.
This repo demonstrates the standard pattern for that: a resource server (a REST API) that validates JWTs on every request, and an MCP server that runs a full OAuth 2.1 authorization flow — login, consent, token exchange — before it lets an MCP client call its tools. Both are deployed as Cloudflare Workers; both are secured by Auth0.
Repository layout
secure-mcp/
├── main.py # placeholder Python entry point (this repo's own package)
├── pyproject.toml # Python project metadata (FastMCP dependency)
├── cloudflare-todoapi-github/
│ └── ai/
│ └── demos/
│ └── remote-mcp-auth0/
│ ├── todos-api/ # Part 1 — Auth0-protected REST API (Hono + Cloudflare Workers)
│ └── mcp-auth0-oidc/ # Part 2 — Remote MCP server with Auth0 OAuth login
└── README.mdcloudflare-todoapi-github/ai is a vendored copy of cloudflare/ai, which ships a large collection of Workers AI / MCP demos. This tutorial focuses on the two folders under demos/remote-mcp-auth0/.
Prerequisites
Node.js 18+ and npm
Python 3.12+ (only needed if you extend the
secure-mcpPython package itself; the two demo projects are pure TypeScript)A free Cloudflare account — Workers' free tier (100k requests/day) is enough for this whole tutorial
A free Auth0 account
curl(ships with Windows 10+ ascurl.exe) or PowerShell'sInvoke-RestMethodThe MCP Inspector — no install needed, run via
npx
Windows / PowerShell users: copy-pasted
curlcommands from documentation use bash syntax (\line continuation, single quotes) that PowerShell doesn't understand. See the PowerShell cheat sheet below before you start pasting commands.
Part 1 — Secure REST API (todos-api)
This is a small Hono app that exposes a few endpoints and validates every request's Authorization: Bearer <token> header against Auth0's public keys (JWKS) before responding.
Route | Auth required | Scope required |
| No | — |
| Yes (valid JWT) | — |
| Yes |
|
| Yes |
|
1. Create the Auth0 API
In the Auth0 Dashboard, go to Applications → APIs → Create API:
Name:
todos-api(or anything)Identifier:
urn:todos-api— this becomes theaudiencevalue everywhere below
Under the API's Permissions tab, add read:todos and read:billing.
2. Configure local secrets
cd cloudflare-todoapi-github/ai/demos/remote-mcp-auth0/todos-api
npm installCreate a .dev.vars file in this folder (never committed — it's in .gitignore):
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=urn:todos-api3. Run it locally
npm run devWrangler starts the Worker at http://127.0.0.1:8789.
4. Get an access token
Create a Machine to Machine application in Auth0 (Applications → Create Application → Machine to Machine), authorize it for the todos-api API, and grant it the scopes you want to test.
$body = @{
client_id = "YOUR_M2M_CLIENT_ID"
client_secret = "YOUR_M2M_CLIENT_SECRET"
audience = "urn:todos-api"
grant_type = "client_credentials"
} | ConvertTo-Json
$response = Invoke-RestMethod -Method Post -Uri "https://your-tenant.us.auth0.com/oauth/token" -ContentType "application/json" -Body $body
$token = $response.access_token5. Call the protected API
Invoke-RestMethod -Method Get -Uri "http://127.0.0.1:8789/api/me" -Headers @{ authorization = "Bearer $token" }You should see the token's claims echoed back (iss, sub, aud, scope, …). A 401 Unauthorized at this point almost always means a typo in AUTH0_DOMAIN/AUTH0_AUDIENCE, or the token's aud claim doesn't match urn:todos-api.
6. Deploy
npx wrangler deployThen set the same secrets on the deployed Worker. Interactive wrangler secret put on Windows is prone to mangling pasted values (stray quotes / trailing newlines end up baked into the secret) — the reliable way is a bulk file:
Set-Content -Path secrets.json -Encoding ascii -Value '{"AUTH0_DOMAIN":"your-tenant.us.auth0.com","AUTH0_AUDIENCE":"urn:todos-api"}'
npx wrangler secret bulk secrets.json
Remove-Item secrets.jsonSecret names are case- and character-exact — AUTH0_DOMAIN and AUTHO_DOMAIN (letter O instead of zero) look identical at a glance in the dashboard and will silently break auth with a confusing 500 Internal Server Error instead of a clean 401:
7. Test the deployment
curl.exe --request GET --url "https://your-worker-name.workers.dev/api/me" --header "authorization: Bearer $token"Part 2 — Secure Remote MCP Server (mcp-auth0-oidc)
This Worker exposes an MCP endpoint at /mcp and wraps it in a full OAuth 2.1 Authorization Code flow: an MCP client that hasn't authenticated gets redirected through Auth0 login and a consent screen before it can call any tool. It uses the todos-api from Part 1 as the downstream resource it acts on behalf of the user.
1. Create an Auth0 Regular Web Application
Applications → Create Application → Regular Web Application.
Under Settings → Allowed Callback URLs, add (comma-separated as you add more later):
http://localhost:8788/callbackNote the Client ID and Client Secret — you'll need both.
2. Authorize this application for the API
This step is easy to miss and produces an opaque error if skipped. Go to Applications → APIs → todos-api → Machine To Machine Applications (or the app's own APIs tab) and explicitly authorize the MCP server's application, ticking the scopes it should be able to request (read:todos, etc.). Without this, Auth0 rejects the login redirect with:
error_description: Client "..." is not authorized to access resource server "urn:todos-api".even though the application type and callback URL are both correct.
3. Create a KV namespace
The OAuth provider uses Workers KV to store authorization state:
cd ../mcp-auth0-oidc
npx wrangler kv namespace create OAUTH_KVCopy the returned id into wrangler.jsonc under the matching kv_namespaces binding.
4. Configure local secrets
npm install --legacy-peer-depsThe
--legacy-peer-depsflag is needed here: theagentspackage pins@cloudflare/workers-types@^4.xwhile the latestwranglerwants^5.x. It's a soft (peerOptional) conflict — safe to bypass, not a real incompatibility.
Create .dev.vars:
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your-regular-web-app-client-id
AUTH0_CLIENT_SECRET=your-regular-web-app-client-secret
AUTH0_AUDIENCE=urn:todos-api
AUTH0_SCOPE=openid profile email offline_access read:todos
API_BASE_URL=http://127.0.0.1:8789
NODE_ENV=developmentPlus a random signing key for the consent-approval cookie:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"COOKIE_ENCRYPTION_KEY=<paste the generated hex string>Without COOKIE_ENCRYPTION_KEY, approving the consent screen fails server-side with Error: cookieSecret is required for signing cookies.
5. Run both Workers together
The MCP server calls the Todos API, so run both at once, in separate terminals:
# terminal 1
cd todos-api
npm run dev # http://127.0.0.1:8789
# terminal 2
cd mcp-auth0-oidc
npm run dev # http://127.0.0.1:87886. Test with the MCP Inspector
# terminal 3
npx @modelcontextprotocol/inspectorThis opens a browser tab with a session-token URL. In the sidebar:
Transport Type:
Streamable HTTP(notSSE, not the defaultSTDIO)URL:
http://localhost:8788/mcpClick Connect
You'll be redirected through Auth0 login, then a consent screen ("MCP Inspector is requesting access…"). Approve it, and the Inspector connects and lists the server's tools.
If Approve fails with "Missing CSRF token cookie": this is a browser issue, not a config issue. The consent cookie is set with
Secureand the__Host-name prefix, which some browsers (Brave with Shields on, in particular) refuse to store onhttp://localhost. Use Chrome/Edge, or disable Shields for the page, and retry the full connect flow (the CSRF token is one-time-use, so a stale attempt won't work — reconnect from scratch).
7. Deploy
npx wrangler deploySet the same variables as secrets on the deployed Worker (via wrangler secret bulk, as in Part 1), then add the deployed callback URL to the Auth0 application's Allowed Callback URLs:
http://localhost:8788/callback, https://your-mcp-worker.workers.dev/callbackSecurity notes
JWT validation is signature-based, not a database lookup. The API fetches Auth0's public keys once (JWKS) and verifies every token's signature,
issuer, andaudiencelocally — no round-trip to Auth0 per request.Scopes enforce least privilege. A valid, correctly-signed token with no
scopeclaim can still authenticate (/api/meworks) but is rejected by scope-gated routes (403 Forbidden) — authentication and authorization are checked separately..dev.varsis local-only. Cloudflare Workers have no filesystem in production;wrangler devreads.dev.vars, but a deployed Worker only sees values pushed viawrangler secret put/wrangler secret bulk. Never commit.dev.vars— it's excluded in this repo's.gitignore.CSRF + signed cookies protect the consent flow. The MCP server's OAuth implementation issues a one-time CSRF token bound to a cookie before rendering the consent form, and signs the "approved clients" cookie with
COOKIE_ENCRYPTION_KEYso it can't be forged client-side.Rotate anything that leaked. If a client secret or access token is ever pasted into a chat log, shared terminal, or committed by mistake, rotate it in the Auth0 dashboard (Application → Settings → Rotate Secret) — treat exposure as compromise regardless of how it happened.
PowerShell cheat sheet
Most MCP/Auth0 tutorials show bash-style curl. On Windows PowerShell, translate:
Bash | PowerShell |
|
|
|
|
|
|
multi-line JSON body | PowerShell here-string: |
For scripted calls, Invoke-RestMethod is usually more convenient than curl — it parses JSON responses into objects automatically:
$response = Invoke-RestMethod -Method Post -Uri "..." -ContentType "application/json" -Body $json
$response.access_tokenTroubleshooting
Symptom | Cause | Fix |
| A dependency pins workers-types v4 while |
|
| Pasted a bash | Replace |
| Hit a route the app doesn't define (e.g. | Check the server's route table in its source |
| Deployed secret values don't exactly match what the token was issued for (typo, stray whitespace) | Re-push secrets with |
| Unhandled exception in the Worker (often a missing/misnamed secret) |
|
Inspector: | Inspector UI kept a stale | In the sidebar, switch Transport Type to |
| Browser refused a | Use Chrome/Edge, or disable Brave Shields for the page; reconnect from scratch |
|
| Generate one, add it, restart |
|
| Add the exact URL (scheme + host + port + path) in Auth0 Application settings |
| The application isn't linked to the API in Auth0 | Authorize the app under the API's Machine To Machine Applications tab, with the needed scopes |
Credits
Demo source: cloudflare/ai —
demos/remote-mcp-auth0Walkthrough reference: Secure and Deploy Remote MCP Servers with Auth0 and Cloudflare (Auth0 blog)
Model Context Protocol specification and Inspector
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mohamedelamraoui1/secure-mcp-cloudflare-auth0'
If you have feedback or need assistance with the MCP directory API, please join our Discord server