Mark Mwangi logoMark
Mwangi
Return to home
Get in
touch
Admin
login
ArchitectureJune 7, 202614 min read

JWT OAuth architecture mistakes

Common pitfalls in JWT-based authentication — signature validation, payload storage, revocation, and token lifetime — and how to avoid them.

By Mark Mwangi
JWT OAuth architecture mistakes cover coverImage

JWTs are popular for stateless authentication, but their simplicity hides architectural traps. A JWT is just a signed token; the moment you misunderstand what "signed" and "stateless" mean, you create real vulnerabilities. This article breaks down the most common mistakes and the patterns that avoid them.

1. Not validating the signature (and algorithm)

A JWT has three parts: header, payload, signature. The signature proves the issuer created it — but only if you verify it with the correct secret/key and algorithm.

ts
// Dangerous: trusting the token without verifying
const payload = JSON.parse(atob(token.split(".")[1]));

// Correct: verify with the library
const verified = jwt.verify(token, publicKey, { algorithms: ["RS256"] });

A classic attack is algorithm confusion: an attacker sends a token signed with HS256 using the public key as the HMAC secret. Pin algorithms to prevent it.

2. Putting secrets in the payload

The payload is base64-encoded, not encrypted. Anyone can read it.

  • Never store passwords, tokens, or PII in a JWT.
  • Treat the payload as public data the server asserts about the user.

If you need confidentiality, encrypt the token (JWE) or keep sensitive data server-side.

3. No revocation strategy

JWTs are stateless, so they're valid until they expire — even after a user logs out or is compromised. Options:

  • **Short access tokens** (minutes) + **refresh tokens** you can revoke.
  • A **denylist** or **version/jti** check for revoked tokens.
  • Rotate refresh tokens and store them server-side.
ts
// Refresh token stored and checked on logout/compromise
const session = await db.sessions.find(refreshTokenId);
if (!session || session.revokedAt) return res.status(401).end();

4. Wrong token lifetimes

  • Long-lived access tokens = big blast radius if leaked.
  • Refresh tokens that never expire = permanent sessions.

Use short access tokens (5–15 min) and refresh tokens with rotation + revocation.

5. Unsafe client storage

  • **localStorage** — vulnerable to XSS token theft.
  • **httpOnly cookies** — not readable by JS, much safer for web.
http
Set-Cookie: refresh=...; HttpOnly; Secure; SameSite=Strict; Path=/auth

Pair with CSRF protection when using cookies.

6. Letting the API surface grow

Keep auth at the edge: a gateway or middleware validates the token once, then passes a trusted identity downstream. Don't re-verify in every service ad hoc.

Mistakes summary

MistakeFix
No signature checkVerify with pinned algorithm
Secrets in payloadKeep payload public-only
No revocationShort access + revocable refresh
Long-lived tokensRotate and expire
localStorage JWThttpOnly, Secure cookies

JWTs are fine — the mistakes are architectural. Validate strictly, keep tokens short-lived, store them safely, and always have a way to revoke.

ArchitectureAuthenticationSecurity
Go back to all blogs →Go to all projects →Contact →

Related Articles

  • Why tutorial culture creates weak developers
  • The case for monorepos in modern web development
  • Git: A Comprehensive Guide for Developers

About

Mark Mwangi

Creating secure and meaningful digital experiences.

Software developer focused on secure, responsive, and high-quality digital experiences.

Socials

GitHubLinkedInXE-mailinstagramfacebookwhatsappMedium

© 2026 Mark Mwangi | All rights reserved

Terms of Service•Privacy Policy

Last updated: April 30, 2026 16:23:51 UTC