Reputation: 101
I have an API (NestJS) and trying to build a login. It is using TypeOrm to access DB.
When the strategy class calls auth.validateUser
method, I need to get a user from DB and compare the passwords using bcrypt
. The problem is that switchMap
is not subscribing to the returned observable.
Here's my code:
auth.service.ts
validateUser(email: string, password: string): Observable<User> {
return this.userService.readByEmail(email).pipe(
switchMap(user => {
return new Observable<User>(subscriber => {
if (!user) {
subscriber.next(null)
subscriber.complete()
} else {
bcrypt.compare(password + global.userPepper, user.password)
.then(result => {
const { password, ...validUser } = user
if (result) subscriber.next(validUser as User)
else subscriber.next(null)
subscriber.complete()
})
.catch(err => {
console.log(err)
subscriber.next(null)
subscriber.complete()
})
}
})
})
)
}
user.service.ts
readByEmail(email: string): Observable<User> {
return new Observable<User>(subscriber => {
// This part of the code is never executed
this.userRepo.findOne({ email }).then(user => subscriber.next(user))
})
}
What is the problem and how can I solve it?
UPDATE:
The problem in userService.readByEmail
was solved following the suggestions from Tobias S. answer.
But the switchMap
operator from authService
was not being called. I solved it changing the strategy file, which calls my auth.validateUser
, to return a promise and not a observable anymore and works.
local.strategy.ts
Before:
validate(email: string, password: string) {
return this.auth.validateUser(email, password).pipe(
map(user => {
if (!user) {
throw new UnauthorizedException()
}
return user
})
)
}
After:
validate(email: string, password: string) {
return this.auth.validateUser(email, password).pipe(
map(user => {
if (!user) {
throw new UnauthorizedException()
}
return user
})
).toPromise()
}
Upvotes: 0
Views: 513
Reputation: 23825
I think this code could be simplified a lot more. You don't really need to create any Observables yourself with new Observable()
.
In UserService you can simply create an Observable from the Promise with the from
operator.
user.service.ts
readByEmail(email: string): Observable<User> {
return from(this.userRepo.findOne({ email }))
}
In AuthService you can return EMPTY
if there is no user and then return the Promise you are creating with bcrypt.
auth.service.ts
validateUser(email: string, password: string): Observable<User> {
return this.userService.readByEmail(email).pipe(
switchMap(user => {
if (!user) return EMPTY
return bcrypt.compare(password + global.userPepper, user.password)
.then(result => {
const { password, ...validUser } = user
if (result) return validUser as User
})
.catch(console.log)
})
)
}
Upvotes: 1