advanced
2 min answer
A team is designing token-based authentication for a distributed system. What are the significant design decisions, and which common JWT choices cause problems later?
Show the full answer Hide the answer
The central trade: stateless versus revocable
A self-contained token that services validate by signature requires no lookup — that is its entire advantage. It is also, for the same reason, valid until it expires, and cannot be revoked.
Every difficulty with JWTs descends from this. A user logs out, an account is disabled, permissions are reduced, a device is lost, a session is compromised — and the outstanding tokens continue to work.
Resolving it
- Short expiry with refresh tokens. Access tokens live minutes; refresh tokens are checked against a store at refresh time. This bounds the revocation window to the access token's lifetime, which is the standard and correct answer for most systems.
- A revocation list for the rare cases, checked at the gateway rather than in every service — small, because entries expire with the tokens.
- Session identifiers instead of self-contained tokens where revocation must be immediate. If every request already hits a shared store, an opaque session identifier is simpler, immediately revocable and does not leak claims. Many systems using JWTs would be better served by sessions and adopted tokens by default.
- A hybrid: opaque tokens externally, exchanged for short-lived signed tokens at the gateway for internal propagation. This is the pattern most large systems converge on, and it gets both properties.
The choices that cause problems later
- Long expiry to avoid implementing refresh. Every security incident then has an unbounded tail.
- Putting authorisation decisions in the token. Roles and permissions embedded at issue time are stale as soon as they change, so a permission revocation does not take effect until expiry. Prefer identity in the token and authorisation evaluated at request time.
- Putting personal data in the token. It is base64, not encrypted, and it is stored in browsers, logged by proxies and sent to third parties.
- Accepting the
algheader from the token. The classic vulnerability class —none, or algorithm confusion between symmetric and asymmetric. The verifier must specify the expected algorithm, not read it from the untrusted input. - No
kidheader or key rotation plan, making key rotation a coordinated outage. - Tokens in local storage, accessible to any script on the page. HttpOnly cookies with appropriate SameSite settings are safer for browser clients, with CSRF handled separately.
- No audience or issuer validation, so a token issued for one service is accepted by another.
- Trusting tokens from other internal services without validation, which reintroduces implicit network trust inside a system that adopted tokens to remove it.
What to validate, every time
Signature, expiry, not-before, issuer, audience, and the algorithm against an expected value. Use a maintained library; hand-rolled validation is where the vulnerabilities are, and the failure is silent because a wrongly-validated token still parses.