Staging worked, production rejected every token. Diffing the two JWTs showed identical structure — except exp: staging tokens lived 24 hours, production 15 minutes, and the reviewer's clock ran 4 minutes slow. Three lessons in one outage: decode shows claims, expiry decides, and clocks lie. This guide teaches checking exp/iat, audience, and the alg:none kill-switch — decoding plus verification, in that order, without ever trusting a token you only read.
Part of the developer toolkit guide. Inspect in the JWT decoder (redact before screenshots); convert epochs in the timestamp converter.
Read claims: exp, iat, aud, iss
| Claim | Means | Check |
|---|---|---|
| exp | Expiry epoch seconds | exp: 1717252800 past = dead, no exceptions |
| iat | Issued-at | Future iat means clock skew or forgery |
| aud | Intended audience | Must equal your service ID exactly |
| iss | Issuer | Must equal your IdP, character-for-character |
| sub | Subject user ID | Opaque string like 123, not an email to trust blindly |
Convert exp in the timestamp converter — epoch integers hide “expired 2 hours ago” behind inscrutable digits. Allow ~30 seconds clock skew (sync NTP on all parties); beyond that, fix clocks, not tolerances.
Kill alg:none on sight
An unsigned token with {"alg":"none"} verifies against nothing — any attacker mints admin claims in seconds. History is littered with libraries that accepted it during algorithm-confusion attacks (attacker swaps RS256→HS256, signs with the public key as HMAC secret). Rules: reject none unconditionally, pin expected algorithms server-side, fetch signing keys only from the IdP's published JWKS over HTTPS. Client-side decoding can display alg; only the backend decides. If your decoder ever shows none on a production token, treat it as an incident, not a curiosity.
Decode ≠ verify (the two-step discipline)
- Decode (client-safe): paste into the decoder, read claims, check expiry/audience/issuer visually. Catches 80% of integration bugs (wrong env, stale token, clock skew).
- Verify (backend only): signature against IdP keys, algorithm allowlist, expiry enforcement, audience match — every request, no caching of verdicts.
- Redact before sharing: tokens in screenshots, tickets and logs leak sessions. Redact signature segments, rotate exposed tokens immediately.
Staging-vs-production mismatches (24h vs 15min lifetimes, different aud) cause most “works here, fails there” mysteries — diff the decoded claims side by side before touching code. Full flag/claim drills in regex groups for log correlation and Base64 modes for segment decoding.
General guidance only, not a security audit. Token architectures for regulated data need professional review.