TheOmniTool logoTheOmniTool
July 7, 2026 · 7 min read

JWTs Explained: How They Work and 6 Mistakes That Get Apps Hacked

What's actually inside a JSON Web Token, how signing works, and the six most common JWT security mistakes developers still make.

JSON Web Tokens are everywhere — login sessions, API auth, OAuth flows. They're also one of the most commonly misused security primitives. Here's how they actually work, and the mistakes that keep showing up in real breaches.

What a JWT actually is

A JWT is three base64url-encoded parts joined by dots:

header.payload.signature

  • Header — metadata, mainly the signing algorithm (e.g. {"alg":"HS256","typ":"JWT"})
  • Payload — the claims: who the user is, when the token expires, etc.
  • Signature — a cryptographic signature over the first two parts

Paste any token into our JWT Decoder and you'll see the header and payload instantly — which brings us to the single most important fact about JWTs.

JWTs are readable by anyone

Base64 is encoding, not encryption. Anyone who obtains a token can read everything in it. The signature only proves the token wasn't modified — it hides nothing. That's mistake #1:

Mistake 1: Putting secrets in the payload

Emails, roles, and user IDs are normal. Passwords, API keys, or personal data are not — they're exposed to anyone who ever sees the token, including browser extensions and logging middleware.

Mistake 2: Not verifying the signature

Decoding a token and trusting its claims without checking the signature means anyone can forge a token claiming to be an admin. Every backend request must verify the signature against your key before trusting a single claim.

Mistake 3: Accepting alg: none

The JWT spec allows an unsigned "none" algorithm. Old or misconfigured libraries would accept a token where an attacker simply stripped the signature and set alg to none. Modern libraries block this — but only if you pin the algorithms you accept: verify with an explicit allowlist like ["HS256"], never "whatever the token says."

Mistake 4: Weak HMAC secrets

HS256 tokens are signed with a shared secret. If that secret is short or guessable ("secret", "changeme", your app's name), attackers can brute-force it offline with tools like hashcat, then mint valid tokens at will. Use a long random secret — 32+ bytes from a password generator or your cloud's secret manager.

Mistake 5: No (or huge) expiration

A JWT is valid until it expires — there's no built-in logout. Tokens without an exp claim, or with week-long lifetimes, mean a stolen token works for a week. Standard practice: short-lived access tokens (5–15 minutes) plus a refresh token that can be revoked server-side.

Mistake 6: Storing tokens in localStorage without thinking about XSS

Any script running on your page can read localStorage. If your app has a single XSS vulnerability, tokens there are gone. HttpOnly cookies avoid that class of theft (while requiring CSRF protection instead). There's genuine debate here, but the wrong answer is not considering the trade-off at all.

The claims worth knowing

The standard claims you'll see when decoding: iss (issuer), sub (subject — usually the user ID), aud (audience), exp (expiry), iat (issued at), nbf (not before). Timestamps are Unix seconds — convert them with the Timestamp Converter.

Quick checklist

  1. Never put secrets in the payload
  2. Always verify signatures, with a pinned algorithm allowlist
  3. Use long random signing secrets
  4. Short expirations + refresh tokens
  5. Think deliberately about where the client stores tokens

Inspect your own tokens with the JWT Decoder — everything runs locally in your browser, so tokens never leave your machine.