Reputation: 48899
I need the following logic but I can't get it. When user is found (not undefined
) I need to compare the password (another promise) which returns a boolean
). Need to:
undefined
(HTTP 404) when user doesn't existForbiddenError
(HTTP 403) when password is wronguser
(HTTP 200) when user exists and password matchesFirst attempt (ugly, unreadable):
@Post()
login(
@BodyParam('username', { required: true }) username: string,
@BodyParam('password', { required: true }) plainPassword: string,
) {
return this.userRepository.findOne({ username: username, enable: true })
.then ((user: User | undefined) => {
if (!user) {
return undefined; // 404
}
return bcrypt.compare(plainPassword, user.password)
.then(passwordMatch => {
if (!passwordMatch) {
throw new ForbiddenError('Authentication failed.'); // 403
}
return user; // 200
});
});
}
Second attempt doesn't work, always returns 'ok'
:
return this.userRepository.findOne({ username: username, enable: true })
.then((user: User | undefined) => {
if (!user) {
return undefined; // 404
}
return bcrypt.compare(password, user.password);
})
.then(passwordMatch => {
// Doesn't work: this is executed every time (even if user is undefined).
return 'ok';
});
Upvotes: 0
Views: 84
Reputation: 1074148
Your then
handler at the end is always run (well, if the promises don't reject) because the first promise resolves with undefined
if the user doesn't exist, or with a boolean if the user does exist.
Your nested version was fine. If you need to return user
in the successful case, it's probably the way to go.
But if you just need to return 'ok'
as in your second code example in the successful case, you could flatten things, you just have to handle the undefined
you'll get if there's no user. We can also take advantage of the fact that you know user
will have the value undefined
if the user wasn't found:
return this.userRepository.findOne({ username: username, enable: true })
// The `then` below returns `undefined` if `user` is `undefined`, the promise from `compare` otherwise
.then((user: User | undefined) => user && bcrypt.compare(password, user.password))
.then(passwordMatch => {
if (passwordMatch === undefined) {
// No user
return undefined;
} else if (!passwordMatch) {
// Bad password
throw new ForbiddenError('Authentication failed.'); // 403
} else {
// All good
return 'ok';
}
});
If you want to flatten it and return user
, then you need to propagate user
to the next handler:
return this.userRepository.findOne({ username: username, enable: true })
.then((user: User | undefined) => {
return !user
? {user} // will be {undefined}
: bcrypt.compare(password, user.password)
.then(passwordMatch => ({user, passwordMatch})); // *** Note making an object
})
.then(({user, passwordMatch}) => { // *** Note destructuring
if (user === undefined) {
// No user
return undefined;
} else if (!passwordMatch) {
// Bad password
throw new ForbiddenError('Authentication failed.'); // 403
} else {
// All good
return user; // 200
}
});
(That first then
handler could be a concise arrow as in the first code block above, but it started getting ugly.)
Upvotes: 3
Reputation: 887305
When you return undefined
, that makes the first then()
callback's promise resolve to undefined
.
Your second then()
callback then executes, receiving undefined
as a parameter.
You should change the second callback to check for that.
Upvotes: 0