Working with JSON Web Tokens (JWT)

1. Understanding JWT Structure

HEADER.PAYLOAD.SIGNATURE
eyJhbGciOi...   eyJzdWIi...   3xJk2P...

Header  : { "alg": "RS256", "typ": "JWT", "kid": "key-2026-01" }
Payload : { "sub": "user_42", "exp": 1715000000, ... }
Sig     : sign(base64url(header) + "." + base64url(payload), key)
      

2. Creating JWT

Example: jsonwebtoken (Node)

import jwt from "jsonwebtoken";
const token = jwt.sign(
  { sub: user.id, role: user.role, scope: "read write" },
  privateKey,
  {
    algorithm: "RS256",
    issuer: "https://auth.example.com",
    audience: "api://orders",
    expiresIn: "15m",
    keyid: "key-2026-01"
  }
);

3. Validating JWT

StepDetail
Parse headerRead alg, kid
Pin algorithmReject if not in allowed list (prevent alg: none)
Fetch keyFrom JWKS endpoint by kid
Verify signatureCrypto check
Validate claimsiss, aud, exp, nbf, iat
Check revocationjti against blacklist if applicable

4. Using JWT Claims

ClaimMeaningExample
issIssuerhttps://auth.example.com
subSubject (user ID)user_42
audAudienceapi://orders
expExpiration (Unix sec)1715000000
nbfNot before1714999000
iatIssued at1714999000
jtiUnique token IDUUID
azpAuthorized party (OIDC)client_id

5. Implementing Custom Claims

PracticeDetail
NamespaceUse URI to avoid collisions: "https://app/roles"
MinimizeOnly what API needs (tokens travel)
No PIIJWT is signed not encrypted — readable
SizeKeep payload < 1KB to fit headers

6. Choosing Signing Algorithms

AlgorithmKey TypeUse Case
RS256RSA 2048+Most common, broad support
ES256ECDSA P-256Smaller sig, modern PREFERRED
EdDSA (Ed25519)Curve25519Fast, modern
PS256RSA-PSSFIPS environments
HS256Shared secretSingle trust domain only
none DEPRECATEDNEVER allow

7. Using Symmetric vs Asymmetric Keys

AspectSymmetric (HS*)Asymmetric (RS*/ES*)
KeyOne shared secretPrivate + public
Verifier needsSame secretPublic key only (JWKS)
DistributionHard at scaleEasy (JWKS endpoint)
Best forSame-service tokensMulti-service, federated

8. Setting JWT Expiration

Token Useexp Recommendation
Access5–15 min
ID Token5–60 min
Service token1–5 min
Email link15–60 min
Clock skewAllow ±60 sec leeway

9. Implementing JWT Refresh Tokens

Note: Refresh tokens themselves should NOT be JWTs — use opaque random strings stored server-side for easy revocation.

10. Preventing JWT Vulnerabilities

VulnerabilityMitigation
alg: nonePin allowed algorithms server-side
Algorithm confusion (RS→HS)Validate alg matches key type
Weak HS256 secret≥256-bit random key
kid injectionValidate kid against allowlist
JWK header injectionIgnore embedded jwk/x5u claims
ReplayShort exp + jti uniqueness
No signature verifyNEVER use jwt.decode() for trust
Stored in localStorageUse cookies (BFF) for browsers

11. Storing JWTs

LocationXSSCSRFVerdict
localStorageStolenSafeAvoid for sensitive
sessionStorageStolenSafeAvoid
Memory onlyLost on reloadSafeOK with refresh
HttpOnly cookieSafeNeed SameSite+CSRFBest for SPAs

12. Using JWE for Encrypted Tokens

AspectJWE (RFC 7516)
PurposeConfidentiality (payload encrypted)
AlgorithmsRSA-OAEP, ECDH-ES (key wrap) + A256GCM (content)
Structure5 parts: header.encKey.iv.ciphertext.tag
When to useTokens containing sensitive PII / claims
PatternNested: JWS-then-JWE (sign then encrypt)