Home > front end >  Passport, Bcryptjs, Async/Await: verify function passes any input in password as true
Passport, Bcryptjs, Async/Await: verify function passes any input in password as true

Time:11-24

Everything in my passport-local strategy seems to be working, except the verifyCallback, where the truthiness evaluation of the validatesPassword function returns true regardless. The problem could stem from the fact that validatesPassword is an asynchronous function and the Promise {<pending>} output while the function works out of order isn't false:

from passwordUtils file:

async function validatesPassword(passwordInput, dbHash) {
    let comparedInput = await bcrypt.compare(passwordInput, dbHash)

    //console.log("this is whats returned from compared Input "   comparedInput)
    return comparedInput;
}

The console.log call in validatesPassword normally prints after the conditional statement in the passport verifyCallback() function in my passport file is called:

 User.findOne(email)
        .then((dbResponse) => {
            //console.log(dbResponse[0][0]);
            let user = dbResponse[0][0];
            if (!user) { return done(null, false); }
            //no error, but also no user 

            const isValid = validatesPassword(password, user.hash);
            //const isValid = false;
            if (isValid) {
                return done(null, user);
            } else {
                return done(null, false);
            }

        })
        .catch((err) => {
            console.log(err);
            done(err);
        });
}


const strategy = new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, verifyCallback);
passport.use(strategy)
...

As seen above, the only way I can derive false from the aforementioned conditional is explicitly setting isValid to false. What would be the best way to get the conditional to wait for the password compare (validatesPassword) function and evaluate its returned boolean function? Should I promisify validatesPassword and add the conditional inside (I've tried implementing this on my own to no avail) of that function and pass all of that to the verifyCallback function in my passport file?

CodePudding user response:

validatesPassword() is an async function. As such it ALWAYS returns a promise.

So, when you do this:

        const isValid = validatesPassword(password, user.hash);
        if (isValid) {
          // ...
        }

That will always be truthy because isValid is a promise so if (isValid) will always pass. Instead, you have to get the value out of the promise with either .then() or await such as:

        const isValid = await validatesPassword(password, user.hash);
        if (isValid) {
          // ...
        }

To use await there, you will have to make the parent function async.


Keep in mind a couple things about the use of async and await:

  1. An async function always returns a promise.
  2. It returns that promise when the async function hits the first await and the execution of that async function is suspended at that point, but it returns the promise immediately and execution of the calling code that receives that promise continues.
  3. Sometime later when the internal promise you are using await with resolves, then the function that was previously suspended at the await will resume executing.
  4. When you eventually get to a return value in that function such as your return comparedInput;, that return value will then be the resolved value of the promise that was previously returned from the async function. So, while this looks like a synchronous return value, it's not. Because the function is async, the return value becomes the resolved value of the promise it returns.
  5. The caller then must use either await or .then() to get the resolved value from the promise.

CodePudding user response:

jfriend00's response got the wheels spinning in my head though I wasn't sure I understood at first. I looked up using conditionals in promises and conditionals that rely on promises and came up with this solution where I .then()'d validatesPassword and then added a conditional within that:

...
User.findOne(email)
        .then((dbResponse) => {
            //console.log(dbResponse[0][0]);
            let user = dbResponse[0][0];
            if (!user) { return done(null, false); }
            //no error, but also no user 
            console.log(
                `
                here's the dbResponse Object:
                user: ${user.username},
                password: ${user.hash},   
                `
            );

            validatesPassword(password, user.hash)
                .then((isValid) => {
                    if (isValid) {
                        return done(null, user);
                    } else {
                        return done(null, false);
                    }
                })
                //const isValid = false;


        })
        .catch((err) => {
            console.log(err);
            done(err);
        });
}
...

I'm not sure if that's what jfriend00 was suggesting but this solution seems to work.

  • Related