Authentication & JWT

🌿 Node.js Lesson 13 Advanced

JSON Web Tokens (JWT) are a compact, self-contained way to securely transmit authentication data between client and server. Combined with bcrypt for password hashing, they form the foundation of modern stateless authentication.

1 Registration & Login Flow
JavaScript — Auth Controller
const bcrypt = require('bcryptjs');
const jwt    = require('jsonwebtoken');
const User   = require('../models/User');

// REGISTER
async function register(req, res) {
  const { name, email, password } = req.body;

  // Check if user exists
  const existing = await User.findOne({ email });
  if (existing) return res.status(409).json({ error: 'Email already registered' });

  // Hash password (cost factor 12)
  const passwordHash = await bcrypt.hash(password, 12);

  const user = await User.create({ name, email, passwordHash });
  res.status(201).json({ id: user._id, name, email });
}

// LOGIN
async function login(req, res) {
  const { email, password } = req.body;

  const user = await User.findOne({ email }).select('+passwordHash');
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  const valid = await bcrypt.compare(password, user.passwordHash);
  if (!valid) return res.status(401).json({ error: 'Invalid credentials' });

  // Sign JWT — expires in 7 days
  const token = jwt.sign(
    { userId: user._id, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: '7d' }
  );

  res.json({ token, user: { id: user._id, name: user.name, email } });
}

module.exports = { register, login };
2 JWT Auth Middleware
JavaScript — Protect Routes
const jwt = require('jsonwebtoken');

function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' });
  }

  const token = authHeader.split(' ')[1];
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded; // attach payload to request
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
}

// Role-based authorization
function authorize(...roles) {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Usage
app.get('/admin', authenticate, authorize('admin'), (req, res) => {
  res.json({ message: 'Admin panel' });
});
3 Code Challenge
Challenge: Add a POST /auth/refresh endpoint that accepts a refresh token (stored in an httpOnly cookie) and returns a new short-lived access token.