Edge Platform
JWT Inspector
Decode, verify, and issue JWTs with Web Crypto on Workers - JWT debugging and experimentation toolkit at the edge
Inspect JSON Web Tokens at the edge: decode header and payload without verification, verify HS256/RS256 signatures, and issue test HS256 tokens for experimentation.
Features
- POST /decode - Parse JWT header and payload (no crypto verification)
- POST /verify - Verify HS256 (secret) or RS256 (public key PEM)
- POST /issue - Mint test HS256 tokens with configurable expiry
- Web Crypto - No external JWT libraries required
API Reference
POST /decode
Decode a JWT without verifying the signature.
Prop
Type
Example Request
curl -X POST "https://your-worker.workers.dev/decode" \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'Success Response
{
"header": { "alg": "HS256", "typ": "JWT" },
"payload": { "sub": "user-1", "exp": 1710000000 }
}Error Codes
400- Invalid JSON (INVALID_BODY), token (INVALID_TOKEN), or decode error (DECODE_ERROR)
POST /verify
Verify a JWT signature.
Prop
Type
Provide secret (HS256) or publicKey (RS256), not both.
Example Request
curl -X POST "https://your-worker.workers.dev/verify" \
-H "Content-Type: application/json" \
-d '{"token":"eyJ...","secret":"demo-secret-key"}'Success Response
{
"valid": true,
"algorithm": "HS256",
"payload": { "sub": "alice", "exp": 1710000000 }
}Error Codes
400- Invalid body/token (INVALID_BODY,INVALID_TOKEN,MISSING_KEY,VERIFY_ERROR)
POST /issue
Issue a test HS256 JWT.
Prop
Type
Example Request
curl -X POST "https://your-worker.workers.dev/issue" \
-H "Content-Type: application/json" \
-d '{"secret":"demo-secret-key","subject":"alice","expiresInSeconds":3600}'Success Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"payload": {
"sub": "alice",
"iat": 1710000000,
"exp": 1710003600,
"iss": "jwt-inspector-demo"
}
}Error Codes
400- Invalid body or secret (INVALID_BODY,INVALID_SECRET)502- Signing failed (ISSUE_ERROR)
Use Cases
- Debug JWT payloads during API integration
- Test HS256/RS256 verification logic before production auth
- Generate short-lived test tokens for local development
Limitations
- Demo tool only; not a production identity provider
- RS256 verification requires PEM public key in request body
- No support for JWKS fetching or exotic algorithms
Use in your project
Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/jwt-inspector.
DependenciesNoneBindingsNonePlatformWeb Crypto
function base64UrlDecode(input: string): string { const padded = input.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((input.length + 3) % 4); return atob(padded);}function base64UrlEncode(input: string): string { return btoa(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");}export function decodeJwt(token: string): { header: Record<string, unknown>; payload: Record<string, unknown>;} { const parts = token.split("."); if (parts.length !== 3) { throw new Error("JWT must have three segments"); } const header = JSON.parse(base64UrlDecode(parts[0])) as Record<string, unknown>; const payload = JSON.parse(base64UrlDecode(parts[1])) as Record<string, unknown>; return { header, payload };}function pemToArrayBuffer(pem: string): ArrayBuffer { const cleaned = pem .replace(/-----BEGIN [^-]+-----/g, "") .replace(/-----END [^-]+-----/g, "") .replace(/\s+/g, ""); const binary = atob(cleaned); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); return bytes.buffer;}async function importHmacKey(secret: string): Promise<CryptoKey> { return crypto.subtle.importKey( "raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"] );}async function importRsaPublicKey(pem: string): Promise<CryptoKey> { return crypto.subtle.importKey( "spki", pemToArrayBuffer(pem), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["verify"] );}export async function verifyJwt( token: string, options: { secret?: string; publicKey?: string }): Promise<{ valid: boolean; algorithm: string; payload: Record<string, unknown> }> { const parts = token.split("."); if (parts.length !== 3) throw new Error("JWT must have three segments"); const { header, payload } = decodeJwt(token); const alg = String(header.alg ?? ""); const data = new TextEncoder().encode(`${parts[0]}.${parts[1]}`); const signature = Uint8Array.from(base64UrlDecode(parts[2]), (c) => c.charCodeAt(0)); if (alg === "HS256") { if (!options.secret) throw new Error("HS256 verification requires secret"); const key = await importHmacKey(options.secret); const valid = await crypto.subtle.verify("HMAC", key, signature, data); return { valid, algorithm: alg, payload }; } if (alg === "RS256") { if (!options.publicKey) throw new Error("RS256 verification requires publicKey PEM"); const key = await importRsaPublicKey(options.publicKey); const valid = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key, signature, data); return { valid, algorithm: alg, payload }; } throw new Error(`Unsupported algorithm: ${alg}`);}export async function issueHs256Token( payload: Record<string, unknown>, secret: string): Promise<string> { const header = { alg: "HS256", typ: "JWT" }; const encodedHeader = base64UrlEncode(JSON.stringify(header)); const encodedPayload = base64UrlEncode(JSON.stringify(payload)); const data = `${encodedHeader}.${encodedPayload}`; const key = await importHmacKey(secret); const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(data)); const encodedSignature = base64UrlEncode(String.fromCharCode(...new Uint8Array(signature))); return `${data}.${encodedSignature}`;}export function validateToken(input: unknown): string | null { if (typeof input !== "string") return null; const trimmed = input.trim(); if (!trimmed || trimmed.split(".").length !== 3) return null; return trimmed;}Deployment
Test your deployment
curl -X POST "https://your-worker.workers.dev/issue" \
-H "Content-Type: application/json" \
-d '{"secret":"demo-secret-key","subject":"alice"}'Local Development
cd apps/experiments/jwt-inspector
npm install
npm run devCloudflare Features Used
- Workers - Edge compute runtime
- Web Crypto API - HMAC and RSA signature operations