Build the exact header, payload, and signature
The encoder serializes each JSON object without whitespace, applies unpadded Base64URL encoding, joins the two segments with a dot, and signs that exact ASCII input. The selected algorithm replaces the header alg value so the control and generated token cannot silently disagree.
Quick actions can add the current iat NumericDate and an exp value one hour later. These values are only editing conveniences. A production issuer should set issuer, subject, audience, lifetime, token identifier, and other claims from its own authenticated server-side policy.
Use browser generation for debugging, not key operations
HMAC signing accepts a UTF-8 or Base64URL-encoded secret. RSA and ECDSA signing accept a PEM PKCS#8 private key, including RSA PRIVATE KEY input converted locally to PKCS#8. The generated token, JSON, and key remain in this browser tab.
Do not paste production secrets or private keys into a general-purpose browser tool. Browser-local processing avoids a network upload, but extensions, clipboard history, screen sharing, device compromise, and reused secrets remain risks. Prefer a maintained JWT library and managed key storage in production.
Common failures this tool explains
- The header or payload is invalid JSON or is not a JSON object.
- The selected algorithm does not match the supplied secret or private-key type.
- A PEM key is incomplete, encrypted, or not in a browser-supported PKCS#8/RSA format.
- A generated token is used as production authentication without server-side issuer, audience, lifetime, and key controls.
Equivalent commands
Use the runtime or shell already available in your workflow. Validate untrusted input and keep binary output out of terminals.
Python
import base64, hashlib, hmac, json
b64 = lambda value: base64.urlsafe_b64encode(value).rstrip(b'=').decode()
body = '.'.join([b64(json.dumps(header, separators=(',', ':')).encode()), b64(json.dumps(payload, separators=(',', ':')).encode())])
token = body + '.' + b64(hmac.new(secret, body.encode(), hashlib.sha256).digest())
JavaScript
import { createHmac } from 'node:crypto';
const b64 = value => Buffer.from(JSON.stringify(value)).toString('base64url');
const body = `${b64(header)}.${b64(payload)}`;
const token = `${body}.${createHmac('sha256', secret).update(body).digest('base64url')}`;
Shell
b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }
body="$(printf '%s' "$HEADER" | b64url).$(printf '%s' "$PAYLOAD" | b64url)"
sig=$(printf '%s' "$body" | openssl dgst -sha256 -mac HMAC -macopt "key:$SECRET" -binary | b64url)