Sign a JWT without leaving your browser
A JSON Web Token (JWT) is three base64url-encoded parts joined by dots: a header, a payload of claims, and a signature. This tool builds all three from JSON you control and signs them with HS256 — HMAC using SHA-256 — using a secret you provide. Everything happens client-side through the browser’s Web Crypto API, so your secret is never transmitted. It is built for developers debugging auth flows, crafting test fixtures, or learning exactly how a JWT is assembled.
How it works
A JWT is computed as:
header_b64 = base64url(JSON header)
payload_b64 = base64url(JSON payload)
signing_input = header_b64 + "." + payload_b64
signature = base64url( HMAC_SHA256(secret, signing_input) )
token = signing_input + "." + signature
The alg field in the header is forced to HS256 so the declared algorithm
always matches the signature that is actually computed — a common source of
“invalid signature” bugs. Base64url is standard base64 with + replaced by
-, / replaced by _, and trailing = padding stripped. The HMAC step
uses crypto.subtle.sign with the UTF-8 bytes of your secret as the key.
Tips and notes
Common payload claims include sub (subject), iat (issued-at, a Unix
timestamp), and exp (expiry). Remember that a JWT is signed, not
encrypted — anyone can base64url-decode the payload and read it, so never
put secrets in the claims. To verify the token elsewhere, use the exact same
secret string. If a verifier rejects your token, check that the secret matches
byte-for-byte and that no extra whitespace crept into the JSON.
What to put in the payload
A minimal payload for a user authentication token typically looks like this:
{
"sub": "user-abc123",
"iat": 1735603200,
"exp": 1735689600,
"iss": "https://auth.example.com",
"aud": "api.example.com"
}
subis the subject — the unique identifier for the user in your system.iatis the issued-at time in Unix seconds (Math.floor(Date.now() / 1000)in JavaScript).expis the expiry time. A common value isiat + 3600for a one-hour token.issidentifies your auth service.audnames the API or service that should accept the token.
Never include passwords, credit card numbers, or other sensitive data in the payload. The payload is only base64url-encoded, not encrypted — any holder of the token can read it in plain text.
Typical uses for this tool
Writing tests. When you need a JWT with specific claims for a unit or integration test — an expired token, a token for a specific user ID, a token with a particular role — this tool lets you build it precisely without spinning up your auth server.
Learning the JWT format. Editing the JSON header and payload directly and watching the resulting token update teaches you how the three segments relate to each other. Changing one character in the payload changes the signature entirely, demonstrating that the HMAC covers both segments.
Debugging “invalid signature” errors. If a verifier rejects a token you have constructed manually, building the same token here with the same secret lets you compare the expected signature against what your code produces.
Verifying the output
The token produced by this tool can be verified with any standard JWT library by passing the same secret:
// Node.js example using jsonwebtoken
const jwt = require('jsonwebtoken');
const payload = jwt.verify(token, 'your-secret-here');
You can also paste the token into the companion JWT Decoder to confirm the header and payload decoded correctly before using it in tests.