Creating JWT Middleware

Now that the registration and login functionality is working, we need middleware to protect any route that requires logging in. This middleware will check for valid JWT tokens and extract the user ID for authorisation decisions.

Add this function to middleware/auth-validation.ts:

import { verifyToken } from "../utils/jwt";

// JWT Authentication Middleware
export function authenticateToken(
  req: Request,
  res: Response,
  next: NextFunction
) {
  const authHeader = req.headers.authorization;

  // Check if Authorization header exists
  if (!authHeader) {
    return res.status(401).json({
      error: "Access token required",
    });
  }

  // Check if header follows Bearer format
  if (!authHeader.startsWith("Bearer ")) {
    return res.status(401).json({
      error: "Token must be in format: Bearer <token>",
    });
  }

  // Extract token from "Bearer <token>"
  const token = authHeader.substring(7);

  // Verify the token
  const payload = verifyToken(token);

  if (!payload) {
    return res.status(403).json({
      error: "Invalid or expired token",
    });
  }

  // Add user info to request object
  req.user = { id: payload.userId };
  next();
}

Any route that uses this middleware will require an Authorisation header with the format Bearer <token> (where <token> is the JWT from login).

What this middleware does

  • Checks Authorisation header: Ensures the request includes an Authorisation header.
  • Validates Bearer format: Confirms the header follows the “Bearer ” format.
  • Extracts token: Removes “Bearer” prefix to get the actual token.
  • Verifies token: Uses verifyToken to check if token is valid and not expired.
  • Adds user info: Puts the user ID on a user property on the request object so our route handlers can access req.user.id to know which user is making the request (we’ll see how to use this below).
  • Continues or blocks: Calls next() if valid, returns an error if invalid.

TypeScript Support

You’ll notice in your editor that there’s a TypeScript warning: Property ‘user’ does not exist on type ‘Request’.

This happens because our middleware adds a user property to the request object, but TypeScript doesn’t know that Express requests can have this custom property. By default, TypeScript only knows about the standard Express Request properties like req.body, req.params, etc.

We need to tell TypeScript that requests can have a user property to avoid these type errors.

Create a file src/types/express.d.ts to extend Express types:

declare global {
  namespace Express {
    interface Request {
      user?: {
        id: number;
      };
    }
  }
}

export {};

This tells TypeScript “requests can have an optional user property containing an id” so we can use req.user.id without TypeScript errors.



Repo link

Tags: