Source: middleware/authMiddleware.js

/**
 * @file Authentication and authorization middleware for Express.
 * @module middleware/authMiddleware
 * @requires jsonwebtoken
 * @requires ../models/userModel
 */

const jwt = require("jsonwebtoken");
const User = require("../models/userModel");

/**
 * Middleware to protect routes by verifying a JWT token.
 *
 * - Looks for a token in the `Authorization` header in the format: `Bearer <token>`.
 * - Verifies the token and decodes the user ID.
 * - Attaches the user (minus password) to `req.user`.
 *
 * @async
 * @function protect
 * @param {Object} req - Express request object. Expects an `Authorization` header.
 * @param {Object} res - Express response object.
 * @param {Function} next - Express next middleware function.
 * @returns {Promise<void>} Proceeds to next middleware if authorized, otherwise responds with 401 Unauthorized.
 */
const protect = async (req, res, next) => {
  let token;

  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith("Bearer")
  ) {
    try {
      // Get token from header
      token = req.headers.authorization.split(" ")[1];

      // Verify token
      const decoded = jwt.verify(token, process.env.JWT_SECRET);

      // Get user from the token payload (ID)
      req.user = await User.findById(decoded.id).select("-password");

      if (!req.user) {
        return res
          .status(401)
          .json({ message: "Not authorized, user not found" });
      }

      next();
    } catch (error) {
      console.error(error);
      res.status(401).json({ message: "Not authorized, token failed" });
    }
  }

  if (!token) {
    res.status(401).json({ message: "Not authorized, no token" });
  }
};

/**
 * Middleware to restrict access to admin-only routes.
 *
 * - Must be used **after** the `protect` middleware.
 * - Checks if `req.user.role` is `"admin"`.
 *
 * @function admin
 * @param {Object} req - Express request object. Requires `req.user` to be set by `protect`.
 * @param {Object} res - Express response object.
 * @param {Function} next - Express next middleware function.
 * @returns {void} Proceeds to next middleware if user is admin, otherwise responds with 403 Forbidden.
 */
const admin = (req, res, next) => {
  if (req.user && req.user.role === "admin") {
    next();
  } else {
    res.status(403).json({ message: "Not authorized as an admin" });
  }
};

module.exports = { protect, admin };