JSON Web Tokens (JWT)

A JSON Web Token (JWT) is a compact, URL-safe way to transmit signed claims between two parties. In web and mobile apps, JWTs are most often used as bearer access tokens for stateless authentication — the server can verify them without a database lookup.

That convenience comes with sharp edges: a JWT is signed, not encrypted, and it stays valid until it expires whether or not the user "logged out." Using JWTs safely is mostly about strict validation and short lifetimes.

TL;DR

Quick Example

Signing and verifying a token in Node.js, with the algorithm pinned and claims enforced:

Core Concepts

Anatomy of a token

A JWT has three parts, separated by dots:

  1. Header — metadata, e.g. the signing algorithm (alg) and type (typ).
  2. Payload — the claims (user id, scopes, expiry…).
  3. Signature — proves the header and payload weren't altered.

⚠️ base64url is not encryption. Anyone holding the token can decode and read the payload. Never put passwords, keys, or PII in it.

Common claims

Registered claims you'll see often:

App-specific claims often add scope/scopes, role/roles, or org_id/tenant_id.

Algorithms: HS256 vs RS256

Rule of thumb: reach for RS256/ES256 as soon as more than one service (or an external party) needs to verify tokens.

Validation Checklist

When your API receives a token, do all of this — skipping any step is a common vulnerability:

  1. Extract it from Authorization: Bearer <token>.
  2. Verify the signature with the correct key.
  3. Pin the algorithm — never accept none, never trust the token's own alg.
  4. Check exp (and nbf if present).
  5. Check iss matches your issuer and aud matches your API.
  6. Apply authorization (roles/scopes) — see API Security.

JWT vs Sessions

JWTs aren't "better sessions" — they're a different trade-off.

Many production apps do both: cookie-backed sessions for the browser, JWT access tokens for service-to-service calls.

Refresh Tokens

Access tokens should be short-lived. For persistent login, pair them with a refresh token:

Storage Guidance

Best Practices

Pin the algorithm

Always pass an explicit algorithms allowlist to your verifier so an attacker can't downgrade to none or swap RS256 for HS256.

Keep tokens short-lived

Short exp windows limit the blast radius of a stolen token, since you can't easily revoke a JWT.

Verify iss and aud

A token minted for another service or audience must be rejected, even if the signature is valid.

Common Mistakes

Accepting any algorithm

Skipping audience/issuer checks

Treating "logout" as revocation

Deleting a token client-side doesn't invalidate it. If you need real revocation, use short lifetimes plus a deny-list or server-side sessions.

FAQ

Are JWTs encrypted?

No. A standard signed JWT (JWS) is only tamper-evident — the payload is base64url and readable by anyone. If you need confidentiality, use JWE, or simply don't put sensitive data in the token.

Can you revoke a JWT?

Not directly — it's valid until exp. Practical options are short lifetimes, a server-side deny-list of token IDs (jti), or rotating the signing key to invalidate everything at once.

Should I use HS256 or RS256?

Use HS256 only when one trusted party both signs and verifies. Use RS256/ES256 when multiple services or external parties verify tokens, so you only distribute the public key.

Where should I store a JWT in a browser?

Prefer an HttpOnly cookie for the refresh token and keep a short-lived access token in memory. localStorage is readable by any XSS, so avoid it for sensitive tokens.

How long should a JWT last?

Access tokens: minutes (5–15). Refresh tokens: days to weeks, rotated on use. Shorter access tokens reduce the damage from theft.

Related Topics

References