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
:
- An
async
function always returns a promise. - It returns that promise when the
async
function hits the firstawait
and the execution of thatasync
function is suspended at that point, but it returns the promise immediately and execution of the calling code that receives that promise continues. - Sometime later when the internal promise you are using
await
with resolves, then the function that was previously suspended at theawait
will resume executing. - 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 theasync
function. So, while this looks like a synchronous return value, it's not. Because the function isasync
, the return value becomes the resolved value of the promise it returns. - 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.