Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I report an invalid login properly with Express and PassportJS?

I've successfully implemented passport-local into my Express/Mongoose web-app but I'm having trouble figuring out how to render a failed login message properly.

Here's my login route:

app.get('/login', function(req, res) {
   res.render('user/login', {
   });
});

With a route like that how am I supposed to report an invalid login? If the login is successful it will write the id/username to the req.user object but that doesn't help me in the "GET /login" route because if it's successful you will get redirected to the page you want to go.

That means req.user will always be undefined when you GET the login page.

I want to be able to write out a message saying something like 'yo, invalid login!' when the following things happen:

  1. The user does not exist.
  2. The password supplied does not match but the user existed.

I might want to output a different message depending on what occurred.

When I implemented the LocalStrategy I used this code:

passport.use(new LocalStrategy({
    usernameField: 'email'
},
function(email, password, fn) {
  User.findOne({'login.email': email}, function(err, user) {
    // Error was thrown.
    if (err) {
      return fn(err);
    }

    // User does not exist.
    if (!user) {
      return fn(null, false);
    }

    // Passwords do not match.
    if (user.login.password != utility.encryptString(user.login.salt + password)) {
      return fn(null, false);
    }

    // Everything is good.
    return fn(null, user);
  });
}
));

As you can see there are some problems but this is how the author of PassportJS set up his application. How are we supposed to access what the Strategy returns?

Like if it throws an error, what am I supposed to even call to get access to err?

Thanks.

like image 907
AntelopeSalad Avatar asked Nov 27 '22 18:11

AntelopeSalad


2 Answers

In the latest version of Passport, I've added support for flash messages, which make it easy to do what you are asking.

You can now supply a third argument to done, which can include a failure message. For example:

if (user.login.password != utility.encryptString(user.login.salt + password)) {
  return fn(null, false, { message: 'yo, invalid login!' });
}

Then, set failureFlash to true as an option to authenticate().

passport.authenticate('local', { successRedirect: '/',
                                 failureRedirect: '/login',
                                 failureFlash: true });

In this case, if authentication fails, the message will be set in the flash, ready for you to render it when you display the login page again.

Custom callbacks are also perfectly fine. The built in options just make it simpler to accomplish common tasks.

Also, I'm curious: you mention that there are problems with the sample. What do you think should be improved? I want to make the examples as good as possible. Thanks!

(For more details, see this comment on issue #12).

like image 192
Jared Hanson Avatar answered Dec 06 '22 12:12

Jared Hanson


You can use the custom callback or middleware functionality to have more control. See the Authentication section of the guide for examples.

For example, a custom callback might look like:

app.get('/login', function(req,res,next) {
    passport.authenticate('local', function(err,user) {
            if(!user) res.send('Sorry, you\'re not logged in correctly.');
            if(user) res.send('Skeet skeet!');
    })(req,res,next);
});

Alternatively, you could always redirect both responses:

app.get('/login', 
    passport.authenticate('local', { successRedirect: '/winner',
                                     failureRedirect:'/loser' }));

Or redirect the failure with simple middleware:

app.get('/login', ensureAuthenticated,
    function(req,res) {
                // successful auth
                // do something for-the-win
    }
);

    // reusable middleware
    function ensureAuthenticated(req,res,next) {
        if(req.isAuthenticated()) {return next();}
        res.redirect('/login/again'); // if failed...
    }
like image 37
Wes Johnson Avatar answered Dec 06 '22 10:12

Wes Johnson