DEV Community

Stack Horizon
Stack Horizon

Posted on

JWT auth without the confusion

JWT auth without the confusion

JWTs are everywhere, but they're often misunderstood. Let's strip away the jargon and see what they actually are, how they work, and how to use them safely in your apps.

What is a JWT?

A JWT (JSON Web Token) is just a string with three parts separated by dots:

header.payload.signature
Enter fullscreen mode Exit fullscreen mode
  • Header: contains the algorithm and token type.
  • Payload: contains claims (data like user id, expiration, etc.).
  • Signature: used to verify the token hasn't been tampered with.

All parts are base64url encoded. The signature is created by hashing the header and payload with a secret (or private key).

How does authentication work with JWTs?

  1. User logs in with credentials.
  2. Server verifies credentials and creates a JWT with user info in the payload.
  3. Server sends the token back to the client.
  4. Client stores the token (usually in memory or localStorage) and sends it in the Authorization header for subsequent requests.
  5. Server verifies the token's signature and expiration, then trusts the claims.

That's it. No session storage, no cookies (if you choose), no server-side state.

The classic pitfalls

1. Storing tokens in localStorage

LocalStorage is accessible to any JavaScript running on your page, making it vulnerable to XSS. If an attacker injects script, they can steal the token.

Better: use httpOnly cookies, which are not accessible to JavaScript. But then you need CSRF protection.

2. Not checking expiration

Always check exp claim. Use a library that validates it automatically.

3. Putting sensitive data in the payload

The payload is base64 encoded, not encrypted. Anyone can decode it. Never put passwords, credit card numbers, or other secrets in there.

4. Using a weak secret

If you use HS256 (symmetric), the secret must be long and random. For production, prefer RS256 (asymmetric) with a private/public key pair.

Minimal working example (Node.js + Express)

Here's a simple implementation using jsonwebtoken:

const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();

const SECRET = process.env.JWT_SECRET || 'change-me';

app.use(express.json());

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // Check credentials (pseudo)
  if (username === 'admin' && password === 'secret') {
    const token = jwt.sign({ sub: username }, SECRET, { expiresIn: '1h' });
    res.json({ token });
  } else {
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

function authMiddleware(req, res, next) {
  const header = req.headers.authorization;
  if (!header || !header.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }
  const token = header.slice(7);
  try {
    const payload = jwt.verify(token, SECRET);
    req.user = payload;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

app.get('/protected', authMiddleware, (req, res) => {
  res.json({ message: 'You are authenticated', user: req.user });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

This is a minimal but functional flow.

When to use JWT vs sessions

JWT is great for:

  • Stateless APIs, especially microservices.
  • Mobile apps where cookies are tricky.
  • Single sign-on across domains.

Sessions (server-side storage) might be better if:

  • You need to revoke tokens instantly (JWTs are valid until they expire unless you maintain a blocklist).
  • You have simple, single-server apps.

Security checklist

  • Use HTTPS in production.
  • Set short expiration times (e.g., 15 minutes) and use refresh tokens.
  • Validate the aud and iss claims if using third-party auth.
  • Keep the secret or private key out of source code.
  • Use established libraries, don't roll your own crypto.

Final thoughts

JWT is not magic. It's a signed token that lets you trust the data it carries. Understand what it does and doesn't protect you from, and you'll avoid most common mistakes. Start with a simple flow, then add refresh tokens and secure storage as your app grows.

Happy coding.

Top comments (0)