I'm trying to implement authentication system described in https://docs.nestjs.com/recipes/passport using Passport Strategies and Guards. When I use AuthGuard('local') to validate and issue jwt tokens to users I end up waiting for response forever. Here are my code snippets
// auth.module.ts
@Module({
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy, RefreshJwtStrategy],
imports: [
UsersModule,
ConfigModule,
PassportModule,
JwtModule.registerAsync({
inject: [ConfigService],
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
secret: configService.get<string>('JWT_SECRET_KEY'),
signOptions: {
expiresIn: configService.get<string>('JWT_ACCESS_TOKEN_TTL'),
},
}),
}),
],
})
export class AuthModule {}
// auth.service.ts
export class AuthService {
constructor(
private readonly userRepository: UsersRepository,
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
/**
* Return user with given username and matching password.
* @param email User's email.
* @param password User's password.
*/
public async validateUser(
email: string,
password: string,
): Promise<User | null> {
const user = await this.userRepository.getUserByEmail(email);
if (user && (await compare(password, user.password))) return user;
return null;
}
public async createTokenPair(user: any): Promise<TokenPairEntity> {
// This is omitted for the sake of brevity.
}
}
// users.repository.ts
@Injectable()
export class UsersRepository {
public constructor(
@InjectModel(User.name) private readonly userModel: Model<User>,
) {}
public async getUserByEmail(email: string): Promise<User | null> {
return this.userModel.findOne({ email }).select(['-__v']).exec();
}
// Other methods are omitted for the sake of brevity.
}
// auth.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Passport, 'local') {
public constructor(private readonly authService: AuthService) {
super({ usernameField: 'email', passwordField: 'password' });
}
public async validate(email: string, password: string): Promise<User> {
const user = await this.authService.validateUser(email, password);
if (!user) throw new UnauthorizedException();
return user;
}
}
// auth.guards.ts
export class LocalAuthGuard extends AuthGuard('local') {}
// auth.controller.ts
@Controller('auth')
export class AuthController {
public constructor(private readonly authService: AuthService) {}
@Post('login')
@UseGuards(LocalAuthGuard)
public async login(@Request() req: Req): Promise<TokenPairEntity> {
return this.authService.createTokenPair(req.user);
}
}
After some debugging I found that my request gets stuck in LocalGuard.canActivate().
// auth.guards.ts
export class LocalAuthGuard extends AuthGuard('local') {
public canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const result = super.canActivate(context);
console.log(result);
return result;
}
}
Here console.log(result) prints Promise { <pending> }. LocalStrategy's validate method is not reached after this.