Script Valley
Authentication From Scratch
JWT AuthenticationLesson 3.2

How to sign and verify JWTs in Node.js

jsonwebtoken package, jwt.sign, jwt.verify, secret key, algorithm options, token expiry, verification errors, algorithm confusion attack

Signing and Verifying with jsonwebtoken

npm install jsonwebtoken
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;

// Sign a token (on login)
const token = jwt.sign(
  { sub: user.id, email: user.email },
  SECRET,
  { expiresIn: '15m', algorithm: 'HS256' }
);

// Verify a token (on protected routes)
try {
  const payload = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
  console.log(payload.sub); // user.id
} catch (err) {
  // TokenExpiredError or JsonWebTokenError
  console.error('Invalid token:', err.message);
}

Always Specify the Algorithm

Always pass { algorithms: ['HS256'] } to jwt.verify. Without it, the library accepts any algorithm the token header specifies. The "alg: none" attack exploits this: an attacker crafts a token with "alg": "none" and no signature. A naive verifier accepts it as valid. Whitelisting the algorithm prevents this entirely.

Expiry Is Non-Negotiable

Always set expiresIn. A token without expiry is valid forever โ€” if it is stolen, you cannot invalidate it short of rotating your secret key (which invalidates all tokens). Typical values: 15 minutes for access tokens, 7 days for refresh tokens.

Up next

Where to store JWTs in the browser

Sign in to track progress

How to sign and verify JWTs in Node.js โ€” JWT Authentication โ€” Authentication From Scratch โ€” Script Valley โ€” Script Valley