A developer's field guide to JSON Web Tokens — what's actually inside one, how to decode safely, and the security pitfalls that bite even experienced engineers.
JSON Web Tokens (JWTs) are everywhere — OAuth flows, API authentication, OpenID Connect, mobile app sessions, microservice handshakes. They're also one of the most misunderstood pieces of modern web security. Senior engineers confidently deploy JWT auth and quietly ship serious vulnerabilities. This guide is what we wish someone had handed us before our first JWT-based login flow.
A JWT is three Base64-URL-encoded parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkphbmUiLCJpYXQiOjE3MTU3MzAwMDB9.dQw4w9WgXcQ_abc123signatureHeader — says which algorithm signed the token (e.g. HS256, RS256).
Payload — the actual claims (user ID, expiry, scopes, custom data).
Signature — cryptographic proof the token wasn't tampered with.
The first two parts are not encrypted. They're just Base64. Anyone can decode them. The signature is what proves authenticity. This trips people up constantly.
Open the Tooloogle JWT Decoder, paste the token, and instantly see:
Header (algorithm, type)
Decoded payload (claims as readable JSON)
Expiry status (in green if still valid, red if expired)
Standard timestamp claims (iat, exp, nbf) converted to your local timezone
The decoder is browser-only — your tokens never reach a server. Critical when you're inspecting production tokens that grant access to real systems.
| Claim | Meaning |
|---|---|
iss | Issuer — who created the token (e.g. https://auth.example.com) |
sub | Subject — who the token is about (usually user ID) |
aud | Audience — who the token is intended for (e.g. my-api) |
exp | Expiry timestamp (Unix seconds) |
iat | Issued-at timestamp |
nbf | Not-before timestamp (token isn't valid until this time) |
jti | Token ID — unique identifier for revocation |
Your auth library validates these. You should never trust a token that hasn't been validated by your library. Decoding alone proves nothing about authenticity.
The single most common JWT mistake: reading the user ID out of the payload without first verifying the signature. An attacker can forge any payload they like — they just can't sign it without your secret. Always call jwt.verify(), never jwt.decode(), in production code.
alg: none BugSome old libraries accept tokens with {"alg": "none"} in the header — meaning "unsigned, trust me". Attackers strip the signature, change the payload, and your code happily accepts it. Always pin the expected algorithm explicitly: jwt.verify(token, secret, { algorithms: ["HS256"] }).
If your code says "verify with this RSA public key" but the library trusts the header's alg field, an attacker can change the header to HS256 and sign with the public key as the HMAC secret. Your code then validates a forged token. Same fix: always pin the algorithm.
JWT payloads are not encrypted, just Base64-encoded. Don't put passwords, API keys, credit card numbers, or anything you wouldn't paste into a public chat. Anyone holding the token can read everything in it.
JWTs are stateless — once issued, they're valid until exp. Always set a short expiry (15–60 minutes), use refresh tokens for long sessions, and have a revocation strategy (token blacklist or short-lived sessions) for compromised accounts.
When a JWT-protected endpoint returns 401:
Decode the token with the JWT Decoder and check exp — is it expired? (Most common cause.)
Check iss and aud — do they match what your API expects?
Check the algorithm in the header — is it the one your verifier expects?
Check clock skew — nbf and exp are evaluated against the server's clock; if the client and server differ by more than the configured leeway, validation fails.
Check the signature key — for RS*, the JWKS endpoint must be reachable and the kid in the header must match a published key.
This 5-step checklist resolves about 95% of "my JWT isn't working" tickets.
JWTs are great for stateless API authentication and inter-service tokens. They're a poor fit for:
Long-lived browser sessions — classic server-side sessions are simpler and easier to revoke.
Storing user state — bigger tokens means more bytes on every request; use a session store with a small session ID.
Anything you need to revoke instantly — without server-side state, revocation is a hard problem.
JWTs are a sharp tool. Used carefully — pin the algorithm, verify the signature, set short expiries, never put secrets in the payload — they're a clean way to do stateless auth across services. Used carelessly, they create entire categories of security bugs. The next time a JWT misbehaves, decode it with the Tooloogle JWT Decoder, walk through the checklist, and most of the mystery dissolves.
Creating helpful tools and sharing productivity insights to make your work easier.
Convert any date into multiple popular formats instantly — DD/MM/YYYY, MM/DD/YYYY, ISO 8601, custom patterns. Free online date format converter.
Send a WhatsApp message to any number without saving it to your contacts. Free, instant, no signup — perfect for businesses and one-off chats.
Generate custom QR codes for URLs, vCards, Wi-Fi, text, and more — high-resolution PNG and SVG download, free.
Calculate the purity percentage and pure gold weight of any jewellery using its karat rating — free and instant.
Convert byte arrays to readable text strings — free online byte-to-text converter with ASCII and UTF-8 support.