Contents
In brief
JWT in Node.js is often shipped “from a tutorial”: data in the payload, long TTL, and Base64 mistaken for encryption. A Dev.to post walks through common failures — from PII in the payload to refresh tokens without rotation — and outlines a solid access + refresh flow.
What happened
The author opens with a real PR: a developer put CPF and a password hash in the JWT, treating Base64 as protection. The reviewer rejected it — and that pattern is common.
JWT is signing, not encryption: header + payload + signature. Anyone with the token reads the payload. Security depends on the secret/key and signature verification, not on hiding data.
Five frequent failures:
| Mistake | Risk | Fix |
|---|---|---|
| Secrets in payload | PII leak via XSS/logs | IDs only; sensitive data in DB |
| alg: none | Algorithm swap, bypass verify | Explicit algorithm allowlist |
| Weak secret | Brute-force signature | ≥256 bits, vault, rotation |
| No exp | Eternal access token | Short access TTL (minutes) |
| No aud/iss | Foreign service token accepted | Check issuer and audience |
Refresh tokens need DB storage, rotation on each refresh, a family ID, and revoking the whole family when one refresh is compromised.
Why it matters
Express/Fastify/Nest apps often get auth “from an npm package in an evening.” JWT looks simple, but mistakes rarely show up in unit tests — pentests, log leaks, or middleware bugs expose them.
Refresh without rotation turns a stolen refresh into a long-lived account key. Payloads with email/roles/PII are a gift to attackers on any XSS.
In practice
- Access — 5–15 minutes; refresh — days/weeks, httpOnly + Secure + SameSite cookie or a rate-limited endpoint.
- In
jwt.verify, setalgorithms: ['HS256'](or RS256) — never trust the client header blindly. - Payload:
sub,roles/scope,jti— no passwords, documents, or payment data. - Refresh: one-time use + DB record; reuse of the same refresh → revoke the family.
- Pre-prod checklist: vault secret, exp/nbf, aud/iss for microservices, never log Authorization.
Takeaway
JWT works as a stateless bearer between services only with discipline: short access, rotated refresh, no secrets in the payload, and strict verify config. The Dev.to article is a useful pre-release checklist for Node APIs.

