Phase 4 · Authentication & SecurityModule 27~66 min read

JWTs, Access Tokens & Refresh Rotation

Use signed tokens only where they fit, validate every claim, rotate refresh tokens, revoke sessions, and prevent replay.

What you'll learn

A JWT is a signed message, not an automatic session strategy. Use short-lived access tokens only when distributed verification helps, validate every relevant claim, and keep refresh-token state revocable.

By the end of this lesson, you'll be able to:

  • Select sessions or tokens by architecture
  • Validate algorithm, issuer, audience, and expiry
  • Rotate refresh tokens
  • Detect replay and revoke token families

Core mental model

Node.js becomes easier when you separate the JavaScript language from the runtime and the operating-system capabilities it exposes. Use this table as a decision guide.

ConceptWhat it meansDecision rule
Access tokenShort-lived authorization evidenceKeep scope narrow and lifetime brief
Refresh tokenLonger-lived credential used to obtain accessStore a server-side hash and rotate every use
Token familyA refresh lineage for one loginRevoke the family when an already-rotated token reappears

Professional workflow

Build and verify Node.js programs from the terminal in small, observable steps.

  1. Define the token lifecycle boundary: inputs, outputs, invariants, ownership, and expected failures.
  2. Design the data or message contract before choosing implementation details.
  3. Implement the smallest correct path with dependencies passed explicitly.
  4. Add validation, failure translation, cleanup, and concurrency behavior.
  5. Verify the boundary with realistic data and at least one adversarial case.
  6. Measure or observe the behavior before optimizing or extracting abstractions.

Keep the feedback loop short

Run the smallest useful command after every meaningful change. Read the complete error message before editing again, and keep inputs and outputs visible while you learn.

Guided code lab

Verify an explicit token contract

A maintained JOSE library handles cryptography while the application pins the expected issuer, audience, and algorithm.

verify-token.js
const { payload } = await jwtVerify(token, publicKey, {
  algorithms: ['RS256'],
  issuer: 'https://auth.example.test',
  audience: 'task-api',
  clockTolerance: 5,
});
if (payload.typ !== 'access' || typeof payload.sub !== 'string') {
  throw new UnauthorizedError();
}

Production practice

Contract

Verification pins cryptographic and semantic claims; refresh exchange is single-use, atomic, hashed at rest, and revocable.

Verification

Test wrong algorithm, key, issuer, audience, type, expiry, future not-before, changed scope, refresh reuse, and family revocation.

Operations

Rotate signing keys with key IDs, keep clocks synchronized, monitor reuse detections, and publish a compromise procedure.

Common failure mode

Decoding a JWT only parses attacker-controlled text. Authorization begins only after signature and claims validation.

Independent workshop

Implement an access/refresh flow with replay detection.

Your finished workshop must include:

  • Documented token claims
  • Pinned verification
  • Short access lifetime
  • Hashed refresh records
  • Atomic rotation
  • Family revocation test

Definition of done

Run the happy path and at least two edge cases, keep responsibilities separated, and add a short README explaining how to run the program.

Recap & quick check

Key takeaways

  • JWTs are signed messages
  • Every claim is a contract
  • Access tokens stay short-lived
  • Refresh tokens remain stateful
  • Replay revokes the family

Quick check

1. Is decoding a JWT authentication?

2. What should happen on refresh reuse?

3. Why pin audience?

Next: Authorization, RBAC & Multi-Tenancy