Decoding is not validating
Anyone can decode a JWT — the payload is just Base64URL-encoded JSON, not encrypted. Validation is what tells you the token is authentic, unexpired, and intended for your service. You must do all of this server-side with your key material. Never validate a token in the browser.
- Splits on
. - Base64URL-decodes each part
- JSON-parses the payload
- Anyone can do this — no key needed
- Verifies the signature with your key
- Checks
exp,nbf,iat - Checks
issandaud - Rejects unexpected algorithms
What every validator must check
Signature
Re-compute the expected signature from the header and payload using your key and the expected algorithm. Compare in constant time. Reject the token if they differ.
Algorithm (
alg)Always specify which algorithms you accept — never derive it solely from the token header. Reject
alg: noneand any algorithm you didn't expect. Use an allowlist, not a blocklist.Expiry (
exp) and not-before (nbf)Check
expagainst the current UTC time. Allow a small clock skew (±60 s). Ifnbfis present, reject tokens presented before that time.Issuer (
iss) and audience (aud)Confirm
issmatches your expected token issuer andaudincludes your service identifier. A valid signature from the wrong issuer should still be rejected.
Validation in six runtimes
All examples validate an HS256 token, checking signature, expiry, issuer, and audience. Swap the algorithm and key type for RS256 or ES256 as needed.
Use jsonwebtoken — the most widely-used Node.js JWT library. Always pass algorithms explicitly.
// npm install jsonwebtoken import jwt from 'jsonwebtoken'; function validateToken(token) { try { const payload = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], issuer: 'https://auth.acme.com', audience: 'api.acme.com', }); return { ok: true, payload }; } catch (err) { return { ok: false, error: err.message }; } } // In an Express middleware: function requireAuth(req, res, next) { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) return res.status(401).json({ error: 'No token' }); const result = validateToken(token); if (!result.ok) return res.status(401).json({ error: result.error }); req.user = result.payload; next(); }
Common validation mistakes
For a full security threat model, see the Security page. For library recommendations, see Libraries.