Cloudflare Workers
Keycard auth for Cloudflare Workers — JWT verification, token exchange, and isolate-safe caching.
The Cloudflare Workers package adapts Keycard’s OAuth primitives to Workers’ fetch(request, env) handler model. It’s the Workers-native equivalent of @keycardai/mcp (Express).
When to Use
Section titled “When to Use”- Deploying an MCP server or API on Cloudflare Workers
- Building Code Mode workers that need Keycard auth
- Any Worker that needs JWT verification + token exchange with isolate-safe caching
Installation
Section titled “Installation”npm install @keycardai/cloudflareKey Exports
Section titled “Key Exports”@keycardai/cloudflare
| Export | Description |
|---|---|
createKeycardWorker | High-level wrapper — chains metadata, auth, and your handler |
verifyBearerToken | Verify JWTs from Request — returns AuthInfo or error Response |
handleMetadataRequest | Serves .well-known OAuth endpoints, returns Response or null |
IsolateSafeTokenCache | Per-user token cache — safe for CF isolate reuse |
resolveCredential | Auto-detect credential type from env bindings |
WorkersClientSecret | Application credential using client ID + secret |
WorkersWebIdentity | Application credential using private key JWT (no secret needed) |
Quick Start
Section titled “Quick Start”import { createKeycardWorker } from "@keycardai/cloudflare";
export default createKeycardWorker({ resourceName: "My MCP Server", scopesSupported: ["mcp:tools"], requiredScopes: ["mcp:tools"],
async fetch(request, env, ctx, auth) { // auth is verified — auth.subject, auth.scopes, auth.token available return new Response(JSON.stringify({ message: "Hello from Keycard!", user: auth.subject, }), { headers: { "Content-Type": "application/json" }, }); },});createKeycardWorker automatically handles:
- CORS preflight responses
- OAuth metadata at
/.well-known/oauth-protected-resourceand/.well-known/oauth-authorization-server - Bearer token verification with JWKS, scope checks, and expiration validation
- JWKS serving at
/.well-known/jwks.jsonwhen using WebIdentity
Delegated Access (Token Exchange)
Section titled “Delegated Access (Token Exchange)”Exchange the user’s Keycard token for an upstream API token using IsolateSafeTokenCache:
import { createKeycardWorker, IsolateSafeTokenCache, resolveCredential,} from "@keycardai/cloudflare";import { TokenExchangeClient } from "@keycardai/oauth/tokenExchange";
let tokenCache: IsolateSafeTokenCache;
function getCache(env) { if (!tokenCache) { const credential = resolveCredential(env); const client = new TokenExchangeClient( env.KEYCARD_ISSUER, credential.getAuth() ?? undefined, ); tokenCache = new IsolateSafeTokenCache(client, { credential }); } return tokenCache;}
export default createKeycardWorker({ async fetch(request, env, ctx, auth) { const cache = getCache(env); const token = await cache.getToken( auth.subject!, auth.token, "https://api.github.com", );
const response = await fetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${token.accessToken}` }, }); return new Response(await response.text()); },});Credential Modes
Section titled “Credential Modes”Client Credentials (client ID + secret)
Section titled “Client Credentials (client ID + secret)”The simplest setup. Create application credentials in Keycard Console and store them as Worker secrets:
wrangler secret put KEYCARD_CLIENT_IDwrangler secret put KEYCARD_CLIENT_SECRETWeb Identity (private key JWT)
Section titled “Web Identity (private key JWT)”No client secret needed. The Worker generates JWT client assertions signed with a private key:
# Generate and store a private keyopenssl genrsa 2048 | wrangler secret put KEYCARD_PRIVATE_KEYThe Worker automatically:
- Serves its public key at
/.well-known/jwks.json - Signs token exchange requests with
private_key_jwt(RFC 7523)
Register the Worker’s JWKS URL in Keycard Console as the application’s public key endpoint.
createKeycardWorker auto-detects which mode to use based on which env vars are set.
Environment Variables
Section titled “Environment Variables”| Variable | Required | Description |
|---|---|---|
KEYCARD_ISSUER | Yes | Keycard zone URL |
KEYCARD_CLIENT_ID | Option A | Application client ID |
KEYCARD_CLIENT_SECRET | Option A | Application client secret |
KEYCARD_PRIVATE_KEY | Option B | PEM-encoded RSA private key |
KEYCARD_RESOURCE_URL | For token exchange | Upstream resource URL |