Glossary

JWT

A JWT (JSON Web Token) is a compact, self-contained token consisting of three Base64-encoded parts (header, payload, signature) that allows a server to verify a token's authenticity without a database lookup.

Explanation

A JWT has three dot-separated parts: header.payload.signature. The header contains the token type and signing algorithm (e.g., {"alg": "HS256", "typ": "JWT"}). The payload contains claims — statements about the user: {"sub": "user-42", "role": "admin", "exp": 1735689600}. The signature is a cryptographic hash of the header and payload, signed with a secret key (HMAC-SHA256) or private key (RS256/ES256). Anyone can read the header and payload (they're Base64-encoded, not encrypted), but the signature proves they haven't been tampered with. When a server receives a JWT, it re-computes the signature using its secret key and compares it to the token's signature. If they match, the token is valid and unmodified. No database lookup needed — the token is self-contained. This is why JWTs are called "stateless": the server doesn't need to store session data. JWT claims to know: iss (issuer), sub (subject/user ID), aud (audience), exp (expiration timestamp), iat (issued-at timestamp). Always set exp — JWTs without expiry are valid forever. Short expiry times (15 minutes for access tokens) with a separate long-lived refresh token mechanism is the standard pattern. JWT pitfalls: if a JWT is stolen, it's valid until it expires (unlike sessions, you can't invalidate it without a server-side denylist). Don't store JWTs in localStorage (accessible to XSS) for sensitive applications — use HttpOnly cookies. The "none" algorithm attack: always explicitly specify and validate the expected algorithm, as some libraries accept algorithm=none (unsigned tokens) if not configured correctly.

Code Example

javascript
// JWT authentication with jsonwebtoken library (Node.js)
const jwt = require('jsonwebtoken');

const SECRET = process.env.JWT_SECRET; // strong random string

// Issue a JWT on login
app.post('/login', async (req, res) => {
  const user = await authenticate(req.body);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

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

  // Safer: HttpOnly cookie (not localStorage)
  res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'lax' });
  res.json({ message: 'Logged in' });
});

// Verify JWT middleware
function authenticate(req, res, next) {
  const token = req.cookies.token || req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token' });

  try {
    req.user = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
}

app.get('/profile', authenticate, (req, res) => {
  res.json({ userId: req.user.sub, role: req.user.role });
});

Why It Matters for Engineers

JWTs are the dominant authentication mechanism for APIs and SPAs. They appear in virtually every production web application. Understanding how they work — what claims are, how signatures are verified, why expiry matters — is essential for both building secure auth systems and for debugging auth bugs. JWT security mistakes are common and serious: weak secrets, missing expiry, storing tokens in localStorage, and trusting the alg header. These vulnerabilities appear in AI-generated auth code. Knowing the correct pattern prevents shipping authentication bugs to production.

Related Terms

Session · Cookies · OAuth · HTTPS

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Web Development Fundamentals → →