Search code examples
node.jsauthenticationmongoosepassport.jscryptojs

Passport fails authentication with "Wrong password" before actually calling my passwordMatch function


This is a weird one.

What im trying to do

Create an authentication server in node.js using Passportjs local strategy, and JWT. Allowing for account registration with email & password, with passwords hashed with 'crypto'

What's happening

So when I login with the right password, to a pre existing model, authentication fails in the APi for having a wrong password. Though theres some weird stuff going on.

What I've tried

Essentially when I make the post request:

  • OPTIONS /api/login calls
  • It goes through my passport config, and in the typical function where you check if the password is correct
  • side note: POST api/login is logged to the console

The function in my passport config:

if (!profileController.passMatch(username, password)) {
            console.log('pass was wrong');
            return done(null, false, {
                message: 'Password is wrong'
            });
        }
  • The 'pass was wrong' thing calls, failing the authentication with done(). Though in passMatch, as you'll see below, it does show the correct password

passMatch function in profile controller:

module.exports.passMatch = (email, password) => {
    User.findOne({email: email}, (err, user) => {
        if (err) { console.log ("error at passMatch: " + err); }
        var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
        console.log(user.hash == hash);
        return (user.hash == hash);
    });
    return false;
};
  • Though if you notice the console log where I check if the hash comparison is correct. That log statement is printed to the console after 'pass was wrong' is logged. It's also printed after the passport.authenticate call in my login function concludes a failed authentication at the console.log(info)

Login function:

module.exports.login = (req, res) => {

    console.log('beginning to authenticate');

    passport.authenticate('local', (err, user, info) => {

        console.log ("authenticating");
        var token;

        // If passport throws an error
        if (err) {
            res.status(404).json(err);
            console.log("error logging in");
            return;
        }

        // If a user is found
        if (user) {

            // Respond with JWT
            token = createJwt(user)
            res.status(200);
            res.json({
                "token": token
            })
            console.log("user logged in");

        // If a user wasn't found
        } else {

            res.status(401).json(info);
            console.log(info);
        }

    })(req, res);

};
  • Error logging in isn't called, but the console.log(info) is called with the error message from the done() message in the config.

What's going wrong here?


Solution

  • In the "passMatch" function, I query for the user again (which is just inefficient), but since this operation was asynch, it was being skipped to the "return false" statement after, and in the passport authentication config process, it recieved that false, causing authentication to fail, but the "log" to be returned after cause it took longer.

    How I fixed it

    I passed in the user object that passport already queried instead of the username into passMatch, then had two operations to check if the hash was the same and returned that, and now it works.

    The new code

    module.exports.passMatch = (user, password) => {
        var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
        return user.hash == hash;
    };
    

    Also the necessary change in the passport config to pass in the user instead of the username as the first param to that function.