NestJS Passport Local ends up never responding

36 views Asked by At

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.

0

There are 0 answers