A JWT looks like gibberish — three base64url-encoded strings separated by dots. But every part has a precise meaning, and misunderstanding JWTs leads to serious security vulnerabilities. This article dissects every component, explains the registered claims the spec defines, and shows what ToollyX's JWT Decoder reveals when you paste one in.
The Three Parts of a JWT
A JWT is exactly three Base64url-encoded JSON objects, separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzA5MDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5ceyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIxMjM0NTY3ODkw...SflKxwRJSMeKKF2QT4fw...Part 1: The Header
The header declares how the token is signed. Two fields are standard:
alg— the signing algorithm. Common values:HS256(HMAC-SHA256, symmetric),RS256(RSA-SHA256, asymmetric),ES256(ECDSA, elliptic curve).typ— token type. Always "JWT".
Critical security note: Never accept "alg": "none" — this means no signature, and was exploited in a famous 2015 JWT attack.
Part 2: The Payload (Claims)
Claims are statements about the subject. The JWT spec defines seven registered claims:
| Claim | Full Name | Type | Notes |
|---|---|---|---|
iss | Issuer | String | Who created the token (e.g., "auth.yourdomain.com") |
sub | Subject | String | Who the token is about (usually user ID) |
aud | Audience | String/Array | Intended recipient(s) — validate this server-side |
exp | Expiration Time | NumericDate | Unix timestamp after which token must be rejected |
nbf | Not Before | NumericDate | Unix timestamp before which token must not be accepted |
iat | Issued At | NumericDate | When the token was issued |
jti | JWT ID | String | Unique identifier — enables token revocation lists |
Everything else in the payload is a private claim — application-specific data like role, email, plan.
Part 3: The Signature
The signature is computed as:
signature = HMAC-SHA256(
base64url(header) + "." + base64url(payload),
secret
)For RS256, the private key signs and the public key verifies — allowing any service to validate a token without knowing the secret. For HS256, the same secret signs and verifies — simpler but requires sharing the secret between all verifiers.
JWT Is Not Encrypted — Anyone Can Read It
Base64url is encoding, not encryption. Anyone who intercepts a JWT can decode the header and payload instantly. Never put sensitive data in JWT claims — no passwords, no credit card numbers, no SSNs. The signature only proves the token wasn't tampered with; it doesn't hide the payload.
If you need confidentiality in the payload, use JWE (JSON Web Encryption) — a different standard that encrypts the payload. Standard JWTs are transparent.
What ToollyX JWT Decoder Shows
Paste a JWT into the JWT Decoder and it:
- Splits the token into its three parts and shows the raw Base64url for each
- Pretty-prints the header and payload JSON with syntax highlighting (keys in purple, string values in green, numbers in blue, booleans in orange)
- Converts
exp,nbf, andiatfrom Unix timestamps to human-readable UTC dates - Shows expiry state: Valid, Expires in <5 minutes, or Expired
- Labels all registered claims with their full names (e.g., "Expiration Time" for
exp) - Allows copying header, payload, or signature individually
The sample JWT in the tool decodes to show Alice Johnson with admin role — useful for testing the tool before pasting your own token.
Common JWT Security Mistakes
- Not validating exp server-side — An expired token should always be rejected, even if the signature is valid.
- Accepting "alg": "none" — Never. Always whitelist specific algorithms.
- Storing JWTs in localStorage — Vulnerable to XSS. Use httpOnly cookies.
- Using a weak secret for HS256 — The secret must be long and random. Generate one with the Password Generator.
- Not validating aud claim — A token issued for your mobile app shouldn't be accepted by your API.
Expiry status, claim labels, syntax highlighting. No data sent to server.