Master coding by building complete, real-world software projects on DevCoach!

Protect API routes using the auth middleware in Express.js

In the first article of this authentication series, Create secure authentication using HTTP-only cookies in Express.js, we outlined the process of creating secure authentication using HTTP-only cookies. In this article, we'll walk through a step-by-step guide to creating an auth middleware and using the middleware to protect Express API routes.

First, install cookie-parser as a dependency and @types/cookie-parser as a dev dependency in your Express.js app. Next, navigate to your app.ts file and add the following code:

import cookieParser from 'cookie-parser';

app.use(cookieParser());

In the above code, we've imported cookieParser and used it as middleware in our Express app, which will help us parse cookies from request headers. Now, within your middleware directory, create an auth.ts file with the following code:

import User from '../models/user';
import jwt, { JwtPayload } from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';

export default async function handler(
  req: Request,
  res: Response,
  next: NextFunction
) {
  if (!req.cookies) {
    console.log('Invalid credentials');
    res.status(401);
    throw new Error('Invalid credentials');
  }

  const { token } = req.cookies;
  if (!token) {
    console.log('Invalid credentials');
    res.status(401);
    throw new Error('Invalid credentials');
  }

  try {
    const decoded = jwt.verify(
      token,
      process.env.JWT_SECRET as string
    ) as JwtPayload;

    const user = await User.findById(decoded._id)
      .select('-__v -password -updatedAt -createdAt')
      .lean();

    if (!user) {
      console.log('Invalid credentials');
      res.status(401);
      throw new Error('Invalid credentials');
    }

    req.user = user;
    next();
  } catch (err) {
    console.log(err);
    throw err;
  }
}

In the above code, we have a handler function that accepts three parameters. The first and second parameters, req and res, are the Express Request and Response objects respectively, and the third parameter, next, is a function that calls the next function/middleware.

Within the handler function, we check for cookies and throw an error if no cookies are found in the request headers. Otherwise, we destructure req.cookies and pull the token out of it. In the first article of this authentication series, we named the cookie as token, which we are accessing here.

Next, we check for the token and throw an error if no token is found in cookies. Otherwise, we create a decoded variable by decoding the token using the jwt.verify method. If the token is valid, we should have a _id property in the decoded object, which we set to the token in the first article of this authentication series.

Next, we query our database and find the user with the _id. We throw an error if no user is found; otherwise, we add the user object in the req object to access it later in our routes. Finally, we call the next function to proceed with the next function/middleware.

Now, let's create a protected route using the auth middleware we just created. So, navigate to the user router and create a /me route with the following code:

import auth from '../middleware/auth';

router.get('/me', auth, async (req, res) => {
  res.status(200).json(req.user);
});

In the above code, we have a route we can use to get a user's details. This is an example of a route that should be protected and can only be accessible by a logged-in user. So, we add the auth middleware between the route and the controller function.

When a request is made to this route, the auth middleware runs before the controller function. The auth middleware does its things, and if the credentials are valid, it adds the user's details to the req object. We can then access and return the user's details from the req object within the controller function.

That's it! You can now use the auth middleware in any route you want to protect. You can also access the user's data, e.g., role from req.user, and create role-based authorization. Hope you learned a thing or two from this article. Feel free to share the article with someone who might also find it useful. Cheers!

Share this article with your friends

Copy URL

Elevate your JavaScript and solopreneur journey

Supercharge your JavaScript skills and solopreneur career. Subscribe now for expert tips and insights!

We use cookies to personalize your site experience.