Audience:
Developers – CISOs – CTOs
JSON Web Tokens (JWT) are one of the most widely adopted authentication mechanisms in modern web applications. They power APIs, microservices, single-page applications, and mobile backends across virtually every industry. But widespread adoption has come with a dangerous side effect: widespread misimplementation.
In our application security engagements at ThreatRepel, JWT misconfiguration is one of the most consistently identified vulnerabilities — across startups, scale-ups, and enterprises alike. The attacks are not sophisticated. They exploit basic mistakes that are entirely preventable.
This post breaks down what JWT is, the correct verification flow every developer must follow, the most dangerous implementation mistakes we see in the field, how to secure your implementation properly, and — critically — what the business consequences are when this goes wrong.
| Key Stat: Auth flaws are consistently ranked in the OWASP Top 10. Broken authentication and session management account for a significant share of publicly disclosed breaches every year. |
What Is JWT and How Does It Work?
A JSON Web Token is a compact, self-contained token format used to securely transmit information between parties as a JSON object. Unlike session-based authentication — where the server stores session state — JWT is stateless. The token itself carries the claims.
A JWT consists of three Base64URL-encoded parts separated by dots:
| Part | Name | Purpose |
| Header | eyJhbGci… | Specifies the algorithm (e.g. RS256) and token type. Must be validated explicitly server-side. |
| Payload | eyJzdWIi… | Contains claims — user ID, roles, expiry, issued-at. Never store sensitive data here. It is Base64 encoded, not encrypted. |
| Signature | SflKxwRJ… | HMAC or RSA signature over header + payload. Verifies integrity. Only as strong as your secret or private key. |
The Correct Verification Flow: Existence → Signature → Claims
This is the most important section of this post. Every protected endpoint must execute these three checks in order, every single request, with no exceptions. Skipping any step — or performing them in the wrong order — creates a vulnerability.
Step 1: Check Token Existence
Before doing anything else, check that a token is actually present. If there is no token, reject the request immediately with a 401 Unauthorized response. Never attempt to process a missing token or fall back to an unauthenticated state silently.
// Node.js example
const token = req.cookies?.accessToken;
if (!token) return res.status(401).json({ error: ‘No token provided’ });
Step 2: Verify the Signature
Cryptographically verify the signature against the header and payload using your server-side secret or public key. Never trust the payload before this step is completed. Never allow the token to specify its own algorithm — attackers can set the algorithm to ‘none’ to bypass signature validation entirely.
// Always specify allowed algorithms explicitly
const decoded = jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
algorithms: [‘RS256’] // Never allow ‘none’
});
Step 3: Validate Claims
After signature verification, validate the claims in the payload. Check that the token has not expired (exp), that it was issued at an expected time (iat), that the audience (aud) and issuer (iss) match your expected values, and that the user’s role or scope grants access to the requested resource.
// Claims validation after signature verification
if (decoded.iss !== ‘https://auth.yourapp.com’) throw new Error(‘Invalid issuer’);
if (decoded.aud !== ‘your-api’) throw new Error(‘Invalid audience’);
if (!decoded.roles.includes(requiredRole)) return res.status(403).json({ error: ‘Forbidden’ });
Critical Mistakes: What Not to Do
1. Skipping Existence Checks
Failing to verify a token exists before processing is a logic error that can create unauthenticated access paths. Always treat a missing token as an immediate rejection.
2. Trusting the Algorithm Field
The “alg: none” attack is well-documented and still exploited in production systems. An attacker strips the signature, sets the algorithm to “none”, and passes a crafted payload. Libraries that do not enforce a specific algorithm will accept it. Always whitelist your allowed algorithms explicitly.
3. Storing JWT in localStorage
localStorage is accessible to any JavaScript running on the page. A single XSS vulnerability — in your code, a third-party script, or an injected ad — gives an attacker full access to every stored token. Use httpOnly cookies instead. They are inaccessible to JavaScript entirely.
4. Setting No Expiry or Long Expiry
A token with no expiry or a multi-day expiry is effectively a permanent credential. If it is stolen — through XSS, a compromised device, or a log leak — the attacker has persistent access until you manually revoke it. Issue short-lived access tokens (15–60 minutes) and use refresh token rotation.
5. Hardcoded or Weak Secrets
Secrets committed to source code end up in version control, CI logs, and build artefacts. Secrets derived from predictable values are brute-forceable. Use cryptographically random secrets of at least 256 bits, stored in environment variables or a secrets manager. Rotate them on a defined schedule.
6. No Token Revocation Strategy
JWT is stateless by design — the server does not track issued tokens. Without a revocation mechanism, a stolen token remains valid until expiry regardless of any action you take. Implement a server-side revocation list (blocklist) for sensitive tokens — particularly those with elevated privileges — and check it on every request.
| ❌ Do Not Do This → Store tokens in localStorage → Skip signature verification → Trust the algorithm field → Set no expiry on tokens → Hardcode secrets in code → Ignore token existence check | ✓ Do This Instead → Store in httpOnly cookies only → Verify signature every request → Lock algorithm to RS256 explicitly → Short expiry + refresh token rotation → Environment-based secrets, rotated → Reject immediately if token missing |
How to Secure JWT: Implementation Best Practices
Use RS256 (Asymmetric) Over HS256 (Symmetric)
HS256 uses a shared secret — the same key that signs a token can verify it. In distributed systems or microservices, every service that needs to verify a token must hold the secret, multiplying exposure. RS256 uses a private key to sign and a public key to verify. Only the auth service holds the private key. Every other service can verify tokens without being able to issue them.
Implement Refresh Token Rotation
Issue short-lived access tokens paired with long-lived refresh tokens. Each time a refresh token is used, issue a new one and invalidate the old one. If a refresh token is used twice — indicating it was stolen and used by an attacker — immediately revoke the entire token family and force re-authentication.
Scope Tokens to Least Privilege
Include only the minimum claims necessary for the requested operation. A token used to read a profile should not carry admin scopes. Design your token claims around specific resource access rather than broad roles wherever possible.
Audit Token Issuance and Rejections
Log every token issuance, successful verification, and rejection with sufficient context — user ID, IP address, timestamp, rejection reason. This data is essential for detecting anomalies, investigating incidents, and satisfying audit requirements.
Rotate Signing Keys
Implement key rotation on a defined schedule. Support JWKS (JSON Web Key Sets) endpoints if you run multiple services, enabling downstream services to automatically fetch updated public keys without redeployment.
| ✓ Implementation Checklist: →Token exists → Signature verified (RS256) → Claims validated → Expiry checked → Audience/Issuer matched → Role/scope Authorized → Request processed |
Business Risk: What JWT Misconfiguration Actually Costs You
For developers, this is a technical problem. For CISOs and CTOs, it is a business risk problem. Understanding the business consequences of auth failures is what drives the organisational investment needed to fix them.
Account Takeover at Scale
A stolen JWT grants an attacker full session-level access to any account. If your tokens carry admin privileges and are stored in localStorage, a single XSS vulnerability becomes a breach of every admin session on your platform. Account takeover at scale leads to data exfiltration, fraudulent transactions, and regulatory reporting obligations.
Compliance Failures
Inadequate authentication controls directly violate requirements under SOC 2, ISO 27001, PCI DSS, HIPAA, and GDPR. For organisations pursuing enterprise contracts or operating in regulated industries, a failed audit or disclosed breach derails sales cycles, triggers fines, and jeopardises certifications.
Loss of Customer Trust
A breach rooted in a preventable auth flaw — one that security professionals consider basic hygiene — is reputationally damaging in a way that is disproportionate to the technical complexity of the mistake. Customers, partners, and prospects interpret it as a signal about your overall security maturity.
Incident Response Cost
The average cost of a data breach in 2024 was $4.88 million globally. Auth failures are among the most common root causes. Beyond direct breach costs, organisations face forensic investigation, legal counsel, regulatory engagement, customer notification, and remediation — all of which are significantly more expensive than prevention.
| 📊 $4.88M — average cost of a data breach (IBM, 2024)80%+ of breaches involve compromised credentials or auth failuresSOC 2, ISO 27001, PCI DSS, HIPAA all require strong authentication controlsEnterprise buyers now routinely include auth security in vendor security questionnaires |
| Is Your Application’s Auth Implementation Secure?ThreatRepel’s Application Security Assessment identifies JWT misconfigurations, broken auth flows, and session management vulnerabilities before attackers do. We deliver actionable findings with clear remediation guidance.→ Book a free consultation at calendly.com/threatrepel |
A Note for CISOs: Making the Case for Auth Security Investment
Authentication and session management are consistently underinvested relative to their risk profile. The controls are well-understood, the implementation cost is low, and the potential impact of failure is existential. Yet auth flaws persist because they are treated as developer-level hygiene rather than strategic security investment.
As a CISO, embedding auth security requirements into your secure development lifecycle — with specific, testable standards around JWT implementation — is one of the highest-ROI security investments available. It reduces breach probability, satisfies compliance frameworks, and gives your engineering teams a clear, auditable standard to build against.
ThreatRepel works with security leadership to translate technical auth findings into risk language that resonates with boards and executive teams — and to build the governance frameworks that ensure these controls are maintained as your architecture evolves.
| Build a Security Program That Starts With the FundamentalsFrom auth security standards to full strategic security program development, ThreatRepel helps organisations build security that delivers measurable business outcomes.→ Connect with us on LinkedIn: linkedin.com/company/threatrepel |
A Note for CTOs: Auth Security as Engineering Culture
The most effective auth security programmes are not audit-driven — they are built into engineering culture. This means documented standards for token handling in your internal developer docs, automated security tests for auth flows in your CI/CD pipeline, and regular penetration testing that specifically targets authentication and session management.
It also means accountability. When auth security is owned by a team, tracked as a metric, and reviewed in architecture decisions, it stops being a vulnerability waiting to be discovered and becomes a competitive advantage — particularly in enterprise sales, where security questionnaires increasingly ask specifically about session management, token handling, and auth standards.
Summary: The Non-Negotiables
- Always verify token existence before any processing — reject immediately if missing
- Always verify the signature server-side, every request — lock to RS256, never allow ‘none’
- Always validate claims after signature — expiry, issuer, audience, role/scope
- Store tokens in httpOnly cookies only — never localStorage or sessionStorage
- Use short-lived access tokens with refresh token rotation
- Store secrets in environment variables or a secrets manager — never in code
- Implement a revocation strategy for high-privilege tokens
- Audit all token events — issuance, verification, rejection
- Rotate signing keys on a defined schedule
- Test your auth implementation — automated and through regular penetration testing
Ready to Secure Your Authentication?
ThreatRepel delivers penetration testing, application security, cloud security, and crisis readiness services — built around business outcomes, not checkbox compliance.
Leave a Reply