Search code examples
node.jsexpressmiddleware

Chained middlware causes Express request to hang forever


I am following a middleware chaining example from this question.

I have a route app.put('/users/:id', isAuthenticated, (req, res) => {db.updateUser(req.params.id, req.body)}. I am trying to write a middleware function that verifies that the ID provided in the URL matches the ID retrieved from the JWT included with the request.

I already have a function isAuthenticated that verifies the JWT and sets res.locals.userId to the UID retrieved; so I would like to simply make use of that in this new function canModifyTarget but for some reason the request hangs forever:

// This function works fine

isAuthenticated: function(req, res, next) {
  let token;
  if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
    token = req.headers.authorization.split(' ')[1];
    admin.auth().verifyIdToken(token).then((decodedToken) => {
      res.locals.userId = decodedToken.uid;
      return next();
    }).catch((error) => {
      return res.status(HttpStatus.UNAUTHORIZED).send();
    })
  }
}

// Switching out isAuthenticated for this in the route causes a permanent hang

canModifyTarget: function(req, res, next) {
  console.log('This is printed');
  return (req, res, next) => {
    console.log('This is NOT printed');
    isAuthenticated(req, res, () => {
      if (req.params.id === res.locals.userId) {
        return next();
      }
      return res.status(HttpStatus.FORBIDDEN).send();
    })
  }
}

Solution

  • middlewares should be callback functions that call "next()" once finished. Your first function, when executed, is calling next() (eventually, after your promise is resolved)

    Your second function isn't calling next(), it is just returning a function definition.

    Define it like this

    canModifyTarget: function(req, res, next) {
        isAuthenticated(req, res, () => {
          if (req.params.id === res.locals.userId) {
            return next();
          }
          return res.status(HttpStatus.FORBIDDEN).send();
        })
      }
    }
    

    and if the third parameter of isAuthenticated is a callback, it should work

    Also, you should define an "else" case in your isAuthenticated function, otherwise it will hang as well (maybe throw an exception or something?)

    If you need to reference them, store them in variables rather than directly defining them in your module.exports:

    const isAuthenticated = function(req, res, next) {
    // code here
    }
    
    const canModifyTarget: function(req, res, next) {
    // code here
     }
    
    module.exports = {
       isAuthenticated,
       canModifyTarget,
    };