After login, every request is looked up or verified.
HTTP forgets you, so each request carries a credential: a random session ID the store looks up, or a signed JWT any service verifies alone.
Anyone can read a JWT; only the issuer can sign one.
Three base64url parts joined by dots. Decoding needs no key, so keep phone numbers out. Any API verifies with the issuer’s public key.
Never let the token say how to verify it
- Pin the algorithm; never accept
none - Keys only from your JWKS, by
kid - Signature first, then
iss,aud,exp,typ - Then your rules: active user, current version
15 min
the longest a stolen access token works
A JWT verifies until exp, even after logout. So access tokens live 5–15 minutes: you rarely revoke them, you stop renewing them.
A thief redeems your stolen refresh token first. Then your app presents the same token.
The trick
Rotate on every use. A used token coming back means a replay, so revoke the whole family.
Where should the browser keep its credential?
Bad: localStorage
Any script reads it; one XSS bug ships it off to replay anywhere.
Good: HttpOnly cookie
Script can’t read it. Pay with CSRF defences: SameSite plus a CSRF token.
Sessions for the browser, short-lived JWTs between services.
One first-party web app? A session: instant revocation, a 43-character cookie. Many services verifying without calling home? JWTs. Many systems use both.
Remember
Prove identity once, remember it per request. Sessions revoke instantly; JWTs verify without a lookup but can’t be recalled, so keep them to 15 minutes and rotate refresh tokens with reuse detection.