MCP OAuth via Auth0 / Google
Browser login for remote MCP — Cursor and Claude Desktop can connect without pasting an API
key. Works alongside client_credentials (API key → mat_ token).
Check if OIDC is live
npm run doctor:oidc
GET /v1/agent → auth.oidc
GET /mcp/auth → oidc: "enabled" | "disabled"
When disabled, use API keys or POST /v1/oauth/token with client_credentials.
1. Auth0 application
- Auth0 Dashboard → Applications → Create → Regular Web Application
- Allowed Callback URLs:
https://api.webmailagent.com/v1/oauth/callback http://127.0.0.1:8787/v1/oauth/callback - Copy Domain, Client ID, Client Secret
2. Deploy secrets (wizard)
npm run wizard:auth0 # paste creds → .dev.vars
npm run wizard:auth0 -- --deploy # + wrangler secrets + contract test
# OIDC_ISSUER example (prod tenant):
# https://webmailagent.us.auth0.com
Browser login uses stateless JWT for authorize state — no Cloudflare KV puts.
After rotating Client Secret in Auth0: update .dev.vars → npm run setup:oidc-prod.
3. Verify discovery
curl -sS https://api.webmailagent.com/.well-known/oauth-authorization-server | jq .
curl -sS -H "Authorization: Bearer $KEY" https://api.webmailagent.com/mcp/auth | jq .
npm run test:contract:qa:oidc # prod contract (skip if still disabled)
Or: npm run setup:oidc-prod from .dev.vars after Auth0 setup.
Flow (authorization_code + PKCE)
- MCP client →
GET /v1/oauth/authorize(PKCE challenge) - Redirect to IdP login
- IdP →
GET /v1/oauth/callback - Client →
POST /v1/oauth/token(authorization_code) - Use
mat_access token onPOST /mcp
SDK
import { MailAgent } from "@mailagent/agent";
const mail = new MailAgent({ baseUrl, apiKey });
const auth = await mail.getMcpAuth();
const { access_token } = await mail.fetchMcpAccessToken(); // client_credentials