Node.js

Auth & JWT – JSON Web Token Authentication

Implement stateless authentication using jsonwebtoken and bcrypt: sign access tokens, verify them in middleware, handle refresh tokens, and secure login routes.

Install: Add jsonwebtoken for token signing/verification and bcryptjs for password hashing.
📄terminal
BASH
npm install jsonwebtoken bcryptjs
Generate Token: Sign a JWT with a payload, secret, and expiry. Store the secret in .env — never hard-code it.
📄src/utils/generateToken.js
JS
const jwt = require('jsonwebtoken');

const generateAccessToken = (user) => {
  return jwt.sign(
    { id: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES_IN || '15m' }
  );
};

const generateRefreshToken = (user) => {
  return jwt.sign(
    { id: user.id },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: '7d' }
  );
};

module.exports = { generateAccessToken, generateRefreshToken };
Verify Middleware: Extract the Bearer token from the Authorization header, verify it, and attach the decoded user to req.user.
📄src/middleware/authMiddleware.js
JS
const jwt = require('jsonwebtoken');

const protect = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token      = authHeader && authHeader.split(' ')[1]; // Bearer <token>

  if (!token) {
    return res.status(401).json({ message: 'No token provided' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    const message = err.name === 'TokenExpiredError'
      ? 'Token expired'
      : 'Invalid token';
    return res.status(403).json({ message });
  }
};

const authorize = (...roles) => (req, res, next) => {
  if (!roles.includes(req.user.role)) {
    return res.status(403).json({ message: 'Forbidden: insufficient role' });
  }
  next();
};

module.exports = { protect, authorize };
Password Hashing: Use bcryptjs to hash passwords before storing them, and bcrypt.compare() to validate on login.
📄src/utils/password.js
JS
const bcrypt = require('bcryptjs');

const hashPassword = async (plainText) => {
  const salt = await bcrypt.genSalt(12); // cost factor
  return bcrypt.hash(plainText, salt);
};

const comparePassword = async (plainText, hashed) => {
  return bcrypt.compare(plainText, hashed);
};

module.exports = { hashPassword, comparePassword };
Login Route: Validate credentials, issue an access token and a refresh token, and return them to the client.
📄src/controllers/authController.js
JS
const { findUserByEmail } = require('../models/User');
const { comparePassword }  = require('../utils/password');
const { generateAccessToken, generateRefreshToken } = require('../utils/generateToken');

const login = async (req, res) => {
  try {
    const { email, password } = req.body;

    const user = await findUserByEmail(email);
    if (!user) return res.status(401).json({ message: 'Invalid credentials' });

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

    const accessToken  = generateAccessToken(user);
    const refreshToken = generateRefreshToken(user);

    // Store refreshToken in DB or httpOnly cookie
    res.cookie('refreshToken', refreshToken, {
      httpOnly: true,
      secure:   process.env.NODE_ENV === 'production',
      sameSite: 'Strict',
      maxAge:   7 * 24 * 60 * 60 * 1000, // 7 days
    });

    res.json({ accessToken, user: { id: user.id, email: user.email } });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
};

module.exports = { login };
Refresh Token Pattern: Validate the refresh token from the cookie and issue a new access token without requiring re-login.
📄src/controllers/authController.js (refresh)
JS
const jwt = require('jsonwebtoken');
const { generateAccessToken } = require('../utils/generateToken');

const refresh = (req, res) => {
  const token = req.cookies?.refreshToken;
  if (!token) return res.status(401).json({ message: 'No refresh token' });

  try {
    const decoded    = jwt.verify(token, process.env.JWT_REFRESH_SECRET);
    const newAccess  = generateAccessToken({ id: decoded.id });
    res.json({ accessToken: newAccess });
  } catch {
    res.status(403).json({ message: 'Invalid or expired refresh token' });
  }
};

module.exports = { refresh };