Startup Cybersecurity // 2026
CybersecurityApril 17, 2026·9 min read

JWT Security for Startups: 9 Mistakes That Turn Auth Into an Incident

JWT can be a perfectly reasonable auth mechanism. It becomes dangerous when teams treat it like a magic string instead of a signed security object with strict validation rules.

The Core Truth

Most JWT incidents happen because teams skip verification details, over-trust claims, or store tokens unsafely in the browser. Safe JWT handling is less about crypto novelty and more about disciplined validation, short lifetimes, scoped claims, and sane session design.

JWT problems are attractive to startups because the initial implementation looks finished. The token is issued, the frontend stores it, requests carry it, and the product works. The security bugs show up later when someone asks whether the signature is verified, whether the token is meant for this service, whether old tokens can be revoked, and whether the role claims are still true.

The right mindset is to treat JWTs as one component of an auth system, not the auth system itself. A signed token still needs storage discipline, claim discipline, and expiry discipline.

9
Common JWT mistakes that show up in startup code
3
Validations every service should enforce
1
Fallback strategy: session cookies beat bad JWT design

Why JWT Incidents Are Usually Operational

Startup teams rarely get breached because an attacker invented new JWT cryptography. They get breached because the implementation skipped a rule that felt optional: using decode instead of verify, failing to pin the algorithm, trusting a role claim forever, leaving expiry too long, or storing tokens where XSS can steal them.

JWT is particularly unforgiving because the failure often looks legitimate. A forged or stale token is still structurally valid JSON. It still deserializes. Without strict verification, it feels like an authenticated request right up to the point it becomes an incident.

If your product does not truly need stateless bearer tokens, session cookies with a strong server-side session model are frequently the simpler and safer option.

The 9 JWT Mistakes Startup Teams Make

Using decode instead of verify

Critical

jwt.decode() parses a token. It does not prove the token is trustworthy. Signature verification is mandatory.

Allowing implicit algorithms

Critical

Your verifier should pin the expected algorithm. Never rely on whatever the token header claims.

Skipping issuer and audience checks

High

A valid token for some other service should not automatically be valid for this one.

Using weak or reused signing secrets

High

Shared low-entropy secrets across environments are one leak away from mass token forgery.

Overlong expiry

High

Tokens that live for days or weeks turn account compromise into a lingering operational problem.

Storing tokens in localStorage without thinking about XSS

Browser

If JavaScript can read the token, any successful XSS can usually steal it.

Trusting role claims forever

Authz

If roles or memberships change, old tokens can preserve privilege longer than intended without refresh or re-check logic.

No revocation or session kill path

IR

If a token leaks, you need a way to invalidate the session context behind it or reduce its usable lifetime.

Treating JWT as a replacement for authorization checks

A01

A user claim is not permission to touch every object. Resource-level authz still matters.

The Difference Between “Works” and “Trustworthy”

The most dangerous JWT code is usually code that works perfectly in development.

Unsafe Validation
import jwt from 'jsonwebtoken';

export function getSession(req: Request) {
  const token = req.headers.get('authorization')?.replace('Bearer ', '');
  if (!token) return null;

  return jwt.decode(token);
}
Strict Validation
import jwt from 'jsonwebtoken';

export function getSession(req: Request) {
  const token = req.headers.get('authorization')?.replace('Bearer ', '');
  if (!token) return null;

  return jwt.verify(token, process.env.JWT_SECRET!, {
    algorithms: ['HS256'],
    issuer: 'custodia.dev',
    audience: 'custodia-app',
  });
}

Verification proves signature integrity and checks that the token was issued for this service under expected conditions. Anything less is faith, not validation.

Even after verification, the application still needs resource-level authorization and session revocation strategy.

When Sessions Are a Better Fit Than JWT

Sessions

You need fast revocation

Server-side sessions make forced logout and access removal simpler when customers or roles change often.

Browser

Your app is mostly browser-based

HttpOnly secure cookies often reduce exposure compared with DIY token handling in JavaScript-heavy frontends.

Design

You do not need cross-service token portability

If the only consumer is your own web app, a session model is often easier to reason about operationally.

IR

You want simpler auth incidents

When a token leak happens, the ability to kill sessions centrally can matter more than the theoretical elegance of stateless auth.

Audit Your Auth Paths

Find the JWT Mistakes That Quietly Become Incidents

Scan the auth layer for weak verification, unsafe storage paths, and the resource-level bugs JWTs never fix on their own.

// npx custodia-cli scan
$ npx custodia-cli scan

  ┌──────────────────────────────────────────────────────┐
  │  CUSTODIA.DEV  //  STARTUP SECURITY ANALYSIS         │
  └──────────────────────────────────────────────────────┘

  HIGH     AUTH-02 jwt.decode used for trust
          src/lib/auth.ts:27
          Token parsed without signature verification before user context is accepted.

  HIGH     AUTH-02 Missing audience check
          src/middleware.ts:41
          JWT verification pins secret but not issuer or audience, widening replay risk across services.

  MEDIUM   DATA-02 Token persisted in browser storage
          src/app/login/page.tsx:66
          Access token written to localStorage in a browser path vulnerable to XSS theft.

  ───────────────────────────────────────────────────────
  OUTPUT: file-level findings, fix guidance, severity map
  COVERAGE: auth, secrets, injection, access control, AI
Scan My CodebaseView Demo Report

Frequently Asked Questions

Is JWT insecure by default?

No. JWT can be secure when implemented correctly. The risk comes from validation gaps, unsafe storage, overly broad trust in claims, and missing operational controls around expiry and revocation.

What is the biggest JWT mistake?

Using decode instead of verify or otherwise failing to validate signature, algorithm, issuer, and audience explicitly. That turns a signed token into an untrusted string you are pretending to trust.

Should JWTs be stored in localStorage?

That depends on the threat model, but many startups underestimate XSS risk. If JavaScript can read the token, XSS often becomes session theft. HttpOnly cookies are frequently safer for browser apps.

Do JWTs replace authorization checks?

No. A valid token proves identity context. It does not prove the right to access a specific document, workspace, or tenant resource.

When should I prefer sessions over JWT?

When your app is primarily browser-based, revocation matters, and you do not need broad token portability between independent services. Simpler auth is often safer auth.

Related Articles
CybersecurityAPI Rate Limiting for StartupsCybersecurityIDOR Vulnerabilities in SaaS AppsCybersecurityPre-Launch Security Checklist for Solo Developers