Search code examples
node.jsexpresspassport.js

Express Passport (node.js) error handling


I've looked at how error handling should work in node via this question Error handling principles for Node.js + Express.js applications?, but I'm not sure what passport's doing when it fails authentication. I have the following LocalStrategy:

passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' },
  function(email, password, next) {
 
    User.find({email: UemOrUnm}, function(err, user){
      if (err) { console.log('Error > some err'); return next(err); }
      if (!user) { console.log('Error > no user'); return next('Incorrect login or password'); } 

      if (password != user.password) {
        return next(Incorrect login or password);
      }
      return next(null, user);
    });
  }
));

After I see 'Error > some err' console printout, nothing else happens. I would think it should continue on the the next path with an error parameter, but it doesn't seem to do that. What's going on?


Solution

  • The strategy-implementation works in conjunction with passport.authenticate to both authenticate a request, and handle success/failure.

    Say you're using this route (which is passed an e-mail address and a password):

    app.post('/login', passport.authenticate('local', {
      successRedirect: '/loggedin',
      failureRedirect: '/login', // see text
      failureFlash: true // optional, see text as well
    });
    

    This will call the code in the strategy, where one of three conditions can happen:

    1. An internal error occurred trying to fetch the users' information (say the database connection is gone); this error would be passed on: next(err); this will be handled by Express and generate an HTTP 500 response;
    2. The provided credentials are invalid (there is no user with the supplied e-mail address, or the password is a mismatch); in that case, you don't generate an error, but you pass a false as the user object: next(null, false); this will trigger the failureRedirect (if you don't define one, a HTTP 401 Unauthorized response will be generated);
    3. Everything checks out, you have a valid user object, so you pass it along: next(null, user); this will trigger the successRedirect;

    In case of an invalid authentication (but not an internal error), you can pass an extra message along with the callback:

    next(null, false, { message : 'invalid e-mail address or password' });
    

    If you have used failureFlash and installed the connect-flash middleware, the supplied message is stored in the session and can be accessed easily to, for example, be used in a template.

    EDIT: it's also possible to completely handle the result of the authentication process yourself (instead of Passport sending a redirect or 401):

    app.post('/login', function(req, res, next) {
      passport.authenticate('local', function(err, user, info) {
        if (err) {
          return next(err); // will generate a 500 error
        }
        // Generate a JSON response reflecting authentication status
        if (! user) {
          return res.send({ success : false, message : 'authentication failed' });
        }
        // ***********************************************************************
        // "Note that when using a custom callback, it becomes the application's
        // responsibility to establish a session (by calling req.login()) and send
        // a response."
        // Source: http://passportjs.org/docs
        // ***********************************************************************
        req.login(user, loginErr => {
          if (loginErr) {
            return next(loginErr);
          }
          return res.send({ success : true, message : 'authentication succeeded' });
        });      
      })(req, res, next);
    });