Backend / Authentication / JWT / 01_jwt_basics.md

JWT Basics — Structure and Encoding

Updated 6 interview angles 5 min read source
On this page10
  1. The anatomy
  2. Base64url — not regular base
  3. A complete example in Python
  4. How JWT is used (typical flow)
  5. Where the token lives
  6. What JWT does well
  7. What JWT doesn’t do well
  8. Common pitfalls (preview)
  9. Common interview confusions
  10. Interview angle

JWT Basics — Structure and Encoding

A JWT is three base64url-encoded segments joined by dots: header.payload.signature. Each segment is a JSON object (the signature is binary). Compact, URL-safe, self-describing — but not encrypted.

The anatomy

text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiQWxpY2UiLCJleHAiOjE3MTYzMjAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└──────── header ──────────┘ └────────────── payload ──────────────┘ └────────── signature ──────────┘

Decode the first two segments at jwt.io — they’re plain JSON. The payload is not secret. Anyone with the token reads everything inside.

json
{ "alg": "HS256", "typ": "JWT", "kid": "abc123" }
Field Means
alg signing algorithm (HS256, RS256, ES256, etc. — see JWT Signing Algorithms)
typ typically “JWT”
kid key ID — which key in your JWKS signed it (for rotation)
cty content type, rare

Payload (claims)

json
{
  "sub": "user_42",
  "name": "Alice",
  "iat": 1716316400,
  "exp": 1716320000,
  "aud": "myapp",
  "iss": "https://auth.example.com"
}

Whatever JSON object you want. Standard claims (registered: sub, iat, exp, aud, iss, nbf, jti) plus your own. See JWT Claims.

Signature

text
HMACSHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

For HS256 (symmetric). For RS256 (asymmetric), it’s RSA-PKCS1-v1_5 with SHA-256 using the private key; verified with the public key.

Critical: the signature signs header + "." + payload. It guarantees:

  • Whoever signed had the secret/private key.
  • Neither header nor payload was modified after signing.

It does NOT guarantee:

  • The content is private (it’s base64-encoded, not encrypted).
  • The token hasn’t been stolen.
  • The user is still allowed to do whatever (auth state may have changed since issuance).

Base64url — not regular base64

text
Regular base64: + / =
Base64url:      - _ (no padding)

URL-safe variant: + and / (which conflict with URLs) become - and _. No padding = characters (they’re optional). Same data, friendlier encoding.

A complete example in Python

python
import jwt
import datetime

# Encode
secret = "supersecretkey"
token = jwt.encode(
    {
        "sub": "user_42",
        "name": "Alice",
        "iat": datetime.datetime.now(datetime.timezone.utc),
        "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=1),
    },
    secret,
    algorithm="HS256",
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# Decode
payload = jwt.decode(token, secret, algorithms=["HS256"])
print(payload)
# {'sub': 'user_42', 'name': 'Alice', 'iat': ..., 'exp': ...}

Always pass algorithms=[...] — never rely on the token’s own alg field. See JWT Signing Algorithms.

How JWT is used (typical flow)

text
1. Client logs in:        POST /login {user, pass}
2. Server validates:      DB lookup, password check
3. Server issues JWT:     signs {sub, exp, ...} → returns token
4. Client stores token:   cookie / memory / secure storage
5. Client sends token:    Authorization: Bearer <token> on every request
6. Server verifies:       check signature, check exp, extract sub → know who the user is
7. Server authorizes:     decide if sub can do the requested operation

Step 6 is local — no DB lookup needed to validate the signature. That’s the JWT pitch: stateless servers, horizontal scaling.

See JWT vs Session-Based Auth for the comparison.

Where the token lives

Storage Pros Cons
localStorage (browser JS) simple XSS reads it — leaks the session
sessionStorage tab-scoped same XSS risk
HttpOnly cookie JS can’t access (XSS-safe) needs CSRF defense
In-memory (SPA) XSS-safe (lost on reload) re-auth on every reload
Mobile secure storage (Keychain/Keystore) best for native only mobile

Modern web app recommendation: refresh token in HttpOnly Secure cookie, access token in memory. See Access and Refresh Tokens.

What JWT does well

  • Stateless backends: any replica can verify without a DB lookup.
  • Microservices: pass the JWT downstream; each service verifies independently.
  • Federated identity: SSO (OIDC ID tokens are JWTs).
  • Self-contained claims: roles / tenant / scopes travel with the token.

What JWT doesn’t do well

  • Revocation: stateless = can’t easily kick someone out before exp. Workarounds (blocklist, short tokens) lose statelessness.
  • Privacy: claims are readable. PII leaks.
  • Long-lived sessions: long-expiry JWT = long exposure window after theft.
  • Mid-session permission changes: token still has old roles until expiry.

For these cases, server-side sessions (opaque session ID + Redis) often beat JWT. See JWT vs Session-Based Auth.

Common pitfalls (preview)

  • alg: none attack — token claims no signature; library accepts.
  • Algorithm confusion (RS256 ↔ HS256).
  • Missing aud / iss validation.
  • PII in payload.
  • No expiry (exp) or excessive lifetime.
  • Storing in localStorage with cookie auth.

Full coverage: JWT pitfalls.

Common interview confusions

  • “JWTs are encrypted.” — signed, not encrypted. Anyone can read the payload. Use JWE if you need encryption. See The JOSE Family — JWS, JWE, JWK, JWA, JWT.
  • “JWTs are inherently more secure than sessions.” — they’re stateless, which is a different property. Sessions with Redis can be equally secure and easier to revoke.
  • “JWTs solve all auth problems.” — they solve stateless auth specifically. They make revocation, mid-session updates, and privacy harder.

Interview angle 6

  • “What is a JWT?” — JSON Web Token: three base64url segments (header.payload.signature). Header declares the signing algorithm. Payload contains claims (user ID, expiry, etc.). Signature proves the token wasn’t tampered with. Self-contained — verifying servers don’t need to look up state.
  • “What does each part contain?” — header: alg and typ. Payload: claims (registered like sub, exp, iss, aud; custom). Signature: HMAC or RSA/ECDSA over the encoded header + payload.
  • “Is a JWT encrypted?” — no, it’s signed. Anyone with the token reads the payload. For encryption, use JWE (rarely seen).
  • “What’s the typical JWT flow?” — client logs in → server issues signed JWT → client sends Authorization: Bearer <jwt> on every request → server verifies signature + claims → extracts user identity → authorizes.
  • “Where should the client store the JWT?” — for web: refresh token in HttpOnly Secure cookie, access token in memory. localStorage is XSS-vulnerable. For mobile: Keychain / Keystore.
  • “Why is JWT compact and URL-safe?” — base64url encoding (no +, /, =); fits in URL params, headers, cookies. Small enough to ride along on every request without bloat.