Search code examples
javascriptnode.jsexpressjwtmiddleware

Can't go to the next middleware - NodeJS


I'm using express and it's router to create a server with a public and private routes. To access the private route user must sign in and send the JWT token. The token is fine but I can't redirect the user to the private route.

const express = require('express');
const jwt = require('jsonwebtoken');

const authRouter = require('./auth');
const usersRouter = require('./users');

const router = express.Router();

router.use(authRouter);

const accessTokenSecret = '4-8-15-16-23-42';
const privateRoutes = ['/user'];

const checkPublicOrPrivate = (req, res, next) => {
  privateRoutes.forEach(route => {
    if (req.path.includes(route)) {
      return authorization(req, res, next);
    }
  });

  return next();
};

const authorization = (req, res, next) => {
  const { token } = req.headers;
  if (token) {
    jwt.verify(token, accessTokenSecret, (err, user) => {
      if (err) {
        return res.status(403).send('Forbiden, access denied');
      }
      console.log(user); //{ username: 'username', iat: 1603288443 }
      return next(); //Should go to the next middleware
    });
  }

  return res.status(403).send('Bad message') //But always hits this message or the 404 one
};

router.use(checkPublicOrPrivate);
router.use(authorization); //Also tried without this line
router.use(usersRouter);

router.use(function(req, res, next) {
  res.status(404).send('404 not found!');
});

router.use(function(err, req, res, next) {
  res.status(500).send('Server error');
});

module.exports = router;

Solution

  • That's because jwt.verify is asynchronous (it has a callback function). While your token is being verified, the code execution goes on. Next line to be executed is res.status(403).send('Bad message'), so it is always executed.

    The solution is to wrap the line in an else statement

    const authorization = (req, res, next) => {
      const { token } = req.headers;
      if (token) {
        jwt.verify(token, accessTokenSecret, (err, user) => {
          if (err) {
            return res.status(403).send('Forbiden, access denied');
          }
          console.log(user);
          return next();
        });
      } else { // add 'else'
          res.status(403).send('Bad message'); // This line will only be executed if there's no token
      }
    };