JWT: Architecture, How It Works, and Security/Performance Trade‑offs
#security#JWT#authentication#authorization#api-design#system-design
JSON Web Tokens (JWT) are a compact way to represent claims (who the user is, what they can do) between parties. They are widely used for API authentication and authorization, especially in stateless, distributed systems.
This note explains JWT architecture, how it works end‑to‑end, and how to control it with security, performance, scalability, and cost in mind.
1. JWT architecture: structure and roles
1.1 Structure
A JWT is a string with three Base64URL‑encoded parts separated by dots:
<header>.<payload>.<signature>
- Header – metadata, typically:
alg: signing algorithm (e.g.HS256,RS256).typ: token type (usuallyJWT).
- Payload – claims about the subject:
- Registered claims:
iss(issuer),sub(subject/user id),aud(audience),exp(expiry),iat(issued at),nbf(not before). - Custom claims: roles, permissions, tenant, anything your app needs.
- Registered claims:
- Signature – protects header+payload against tampering:
- HMAC (symmetric key) or asymmetric (RSA/ECDSA) over
base64url(header) + '.' + base64url(payload).
- HMAC (symmetric key) or asymmetric (RSA/ECDSA) over
JWTs are usually carried in the Authorization: Bearer <token> header.
1.2 Actors in a JWT‑based system
- Client – browser, mobile app, service.
- Auth server / IdP – issues tokens after authenticating the user (login, MFA, etc.).
- Resource server / API – validates tokens and enforces access control.
Architecture pattern:
User -> Auth Server (login) -> JWT -> Client
Client -> API (Bearer JWT) -> Validate -> Authorize -> Response
2. How JWT works end‑to‑end
2.1 Issuing a token (authentication)
- User authenticates (username/password, SSO, OAuth2/OIDC flow).
- Auth server builds claims (user id, roles, tenant, etc.).
- It signs the token with its private key / shared secret.
- Client receives token (often with a refresh token if using OAuth2/OIDC).
2.2 Using a token (authorization)
On each protected API call:
- Client sends HTTP request with
Authorization: Bearer <JWT>. - API parses the token and:
- Verifies the signature with the correct key.
- Checks standard claims (
iss,aud,exp,nbf). - Applies authorization logic based on custom claims (roles, scopes, permissions).
- If valid, API processes the request; if not, returns
401/403.
2.3 Refreshing and revoking
Because JWTs are usually stateless, you can’t change or revoke them easily once issued. Common pattern:
- Use a short‑lived access token (e.g. 5–15 minutes).
- Pair it with a longer‑lived refresh token stored more securely.
- When the access token expires, the client calls the auth server with the refresh token to get a new one.
- Revoke access by:
- Revoking / blacklisting refresh tokens.
- Shortening access token TTL so old tokens expire quickly.
3. Security: how to do JWT safely
3.1 Signing algorithms and keys
- Prefer asymmetric algorithms (e.g.
RS256) for larger systems:- Auth server keeps private key; APIs use public key to validate.
- Easier key rotation: publish new public keys via a JWKS endpoint.
- Avoid
nonealgorithm and insecure algorithms. - Protect signing keys:
- Use HSMs, KMS, or secret managers.
- Rotate keys regularly; support multiple keys (kid) during rotation.
3.2 Validations APIs must perform
On every request the API should:
- Verify signature with the correct key (and algorithm).
- Check issuer (
iss) – must match your auth server. - Check audience (
aud) – must match this API / client. - Check expiry (
exp) and not before (nbf). - Optionally enforce clock skew tolerance (e.g. ±30 seconds).
- Apply authorization logic:
- Scopes:
scp,scopeclaims. - Roles/permissions: custom claims (e.g.
roles,permissions).
- Scopes:
Never trust the payload without verifying the signature and claims.
3.3 Token storage and transport
- Always use HTTPS – never send tokens over plain HTTP.
- For browser apps:
- Avoid long‑lived tokens in localStorage (XSS risk).
- Prefer HTTP‑only, secure cookies or in‑memory storage plus short lifetimes.
- Consider CSRF protections if using cookies (same‑site, CSRF tokens).
3.4 Revocation strategies
Because JWTs are stateless, you need explicit strategies:
- Short expiry – main defence; makes tokens self‑revoking quickly.
- Token blacklist / denylist:
- Store revoked JTIs (token IDs) or refresh tokens in a central store (e.g. Redis).
- APIs check blacklist for high‑risk operations or within a small window.
- Rotate refresh tokens and invalidate the old one upon use.
Trade‑off: the more central checks you add, the less “stateless” your system becomes—but you gain control.
4. Performance and scalability
4.1 Stateless validation (no DB lookup per request)
With JWT, APIs can usually validate locally:
- Get the public key from a JWKS endpoint on startup (or cache and refresh periodically).
- Verify signature + claims entirely in process.
Benefits:
- Low latency – validation is just CPU work (crypto + JSON parsing).
- Scales horizontally – add more API instances without additional shared state.
- Reduced DB load – no per‑request session lookup.
4.2 Token size and impact
JWTs can grow large if you pack too many claims:
- Larger tokens → higher bandwidth per request (esp. on mobile).
- May hit header size limits if passed as HTTP headers (common).
Recommendations:
- Keep only what you need in the token (subject id, roles/permissions, tenant id).
- Put large or volatile data (profiles, settings) behind a separate, cacheable API.
4.3 Caching keys and metadata
- Cache the JWKS (JSON Web Key Set) from the auth server, with TTL.
- Handle key rotation:
- Use the
kidheader to pick the correct public key. - Refresh keys if validation fails for a valid-looking token.
- Use the
This avoids calling the auth server on every request but still supports rotation.
5. Cost considerations
JWT can reduce infrastructure cost in large systems:
- Less central session storage (no big in‑memory session cluster).
- APIs can be stateless, making them easier to scale and schedule on Kubernetes or serverless.
- Auth server load is mostly on login / refresh, not on every API request.
However, costs can appear elsewhere:
- CPU cost for signature verification:
- Asymmetric crypto is slower than symmetric HMAC.
- Use benchmarks to size your API instances; consider HMAC (
HS256) only when sharing the secret is acceptable and you control all services.
- Network cost:
- Large tokens sent on every request increase bandwidth charges in cloud environments.
Optimisation tips:
- Use short, compact claims and consistent naming.
- Consider lower‑cost signing algorithms only if they meet your security requirements.
- Offload some validation to an API gateway or service mesh that can centrally validate tokens.
6. Design guidelines and pitfalls
Do:
- Use short‑lived access tokens and refresh tokens.
- Validate all critical claims (
iss,aud,exp,nbf). - Use asymmetric keys and automated key rotation.
- Keep tokens as small as practical; store extra data elsewhere.
Avoid:
- Storing sensitive data (passwords, secrets) inside JWT claims.
- Using JWT as a general‑purpose session store for mutable data.
- Relying on long‑lived tokens without refresh or revocation mechanisms.
- Accepting tokens without verifying the signature or algorithm.
7. Quick interview / design checklist
When discussing JWT in architecture or interviews, cover:
- What JWT is and how the
<header>.<payload>.<signature>structure works. - Who issues tokens, where validation happens, and how keys are managed (JWKS, rotation).
- Security: HTTPS, claim validation, short TTLs, refresh tokens, revocation strategies.
- Performance & scalability: stateless validation, local key cache, token size, CPU cost of crypto.
- Trade‑offs vs sessions: simpler horizontal scaling vs harder revocation and more complex security design.
Used thoughtfully, JWT gives you a stateless, scalable way to propagate identity and authorization across microservices and APIs—while still keeping security, performance, and cost under control.
Comments