LocalAuthGuard not working in nestjs app with typeorm and passport-local

Viewed 933

I am using nestjs 8.0 with typeorm, passport-jwt, and passport-local. Everything seems to be working fine other than the LocalAuthGuard. I am able to successfully create a new user and even use the routes that have JwtAuthGuard but LocalAuthGuard seems to have some issues as I keep getting 401 Unauthorized error

Also, is there a way to console log some output from within the LocalAuthGuard or LocalStrategy?

auth.controller.ts

@Controller(['admin', 'user'])
export class AuthController {
  constructor(
    private authService: AuthService,
  ) {}

  @UseGuards(LocalAuthGuard)
  @Post('login')
  login(@Request() req) {
    console.log('object');
    if (req.path.includes('admin') && !req.user.isAdmin) {
      throw new UnauthorizedException();
    }
    return this.authService.login(req.user);
  }

...
}

local.guard.ts

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

local.strategy.ts

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(usernameOrEmail: string, password: string): Promise<any> {
    const user = await this.authService.validateUser({
      usernameOrEmail,
      password,
    });
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

auth.service.ts

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private jwtService: JwtService,
  ) {}

  async validateUser({ usernameOrEmail, password }: LoginDto) {
    const user = (await this.userRepository.findOne({ username: usernameOrEmail })) ||
  (await this.userRepository.findOne({ email: usernameOrEmail }));
    if (user && (await bcrypt.compare(password, user.password))) {
      return user;
    }

    return null;
  }
...
}

auth.module.ts

@Module({
  imports: [
    TypeOrmModule.forFeature([User]),
    PassportModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        return {
          secret: configService.get('JWT_KEY'),
          signOptions: {
            expiresIn: '6000s',
          },
        };
      },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}

Any help or suggestion is greatly appreciated.

EDIT

It seems for LocalAuthGuard, username, and password are a must and other properties are optional.

1 Answers

You can create multiple local strategies with different parameters. For example,

  1. Username and password
  2. Phone and OTP
  3. Email and password
  4. Email and OTP

For using multiple local strategies, refer to this answer

Then, you can also pass an options object to specify different property names, for example: super({ usernameField: 'email', passwordField: 'otp' })

implement local strategy as follows

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { IAuthService } from '../services';
import { LoginDto, OtpLoginDto } from '../dto';
import { UserDto } from 'src/modules/user/dto';
import { plainToClass } from 'class-transformer';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(
    @Inject(IAuthService)
    private readonly authService: IAuthService,
  ) {
    super({
      usernameField: 'user_name',
      passwordField: 'otp',
    });
  }

  async validate(user_name: string, otp: string): Promise<any> {
    const loginDto = plainToClass(OtpLoginDto, {
      username: user_name,
      otp: otp,
    });
    const user: UserDto = await this.authService.verifyOtp(loginDto);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

ref: customize passport

nestjs docs

Related