The server doesn’t remember you. Every request carries proof of who you are. That’s the point of a token.

The Structure#

A JWT is three base64url-encoded segments joined by dots: header, payload, signature. The header says which algorithm signed it. The payload carries claims: user ID, roles, expiry time. The signature is a cryptographic proof that the header and payload haven’t been tampered with.

The server doesn’t need a database lookup to verify a JWT. It re-computes the signature using its secret key and checks it matches. Stateless verification. That’s why tokens scale better than session IDs.

Short-Lived Access, Long-Lived Refresh#

Access tokens expire in minutes (15 minutes is common). Refresh tokens expire in days or weeks. The client uses the refresh token to get a new access token when the old one expires, without asking the user to log in again. When you revoke a refresh token, you can’t revoke the access tokens already issued, but you limit the damage window to however long the access token lives.

This is why access token lifetime matters. An hour-long access token means a compromised credential stays valid for up to an hour after you detect the breach.

JWT Pitfalls#

The most embarrassing one: accepting alg: none. Some early JWT libraries trusted the algorithm specified in the header, meaning an attacker could set alg: none, remove the signature, and get accepted. Libraries fixed this years ago, but you should explicitly specify which algorithms are acceptable rather than trusting the token to tell you.

graph TD A[User Logs In] --> B[Auth Server Issues Access Token 15min + Refresh Token 30days] B --> C[Client Stores Tokens] C --> D[Request with Access Token] D --> E{Token Valid?} E -->|Yes| F[Serve Request] E -->|Expired| G[Use Refresh Token to Get New Access Token] G --> D E -->|Invalid| H[Force Re-Login] style A fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style B fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style C fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style D fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style E fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style F fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style G fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style H fill:#000000,stroke:#ff0000,stroke-width:2px,color:#fff

At Salesforce#

We had a session token that wasn’t invalidated on logout. The endpoint cleared the cookie in the browser, but the token itself was still valid. A user reported their account accessed after they’d logged out on a shared computer. Someone had captured the token from the network tab before logging out and used it afterward. The fix was moving to short-lived access tokens and validating against a revocation list on the auth service.

What I’m Learning#

Tokens feel like a solved problem until you have to revoke one. The next post covers that specific headache.

What’s the access token lifetime you’ve settled on in production, and how did you land on it?