0

I'm trying to implement JWT with NestJs. In my user.module.ts, I have added following configuration:

import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { UserService } from './user.service';
import { UserResolver } from './user.resolver';
import { User } from './entities/user.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthHelper } from './auth/auth.helper';
import { JwtStrategy } from './auth/auth.strategy';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [
    // PassportModule.register({ defaultStrategy: 'jwt', property: 'user' }),
    // JwtModule.registerAsync({
    //   inject: [ConfigService],
    //   useFactory: (config: ConfigService) => ({
    //     secret: 'secret',
    //     signOptions: { expiresIn: 36000000 },
    //   }),
    // }),
    TypeOrmModule.forFeature([User]),
  ],
  providers: [UserResolver, UserService], // AuthHelper, JwtStrategy],
})
export class UserModule {}

Whenever I uncomment these lines, I get some issues.

Here are some relevant files:

auth.strategy.ts

import { Injectable, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from '../entities/user.entity';
import { AuthHelper } from './auth.helper';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  @Inject(AuthHelper)
  private readonly helper: AuthHelper;

  constructor(@Inject(ConfigService) config: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'KhubSecret',
      ignoreExpiration: true,
    });
  }

  private validate(payload: string): Promise<User | never> {
    return this.helper.validateUser(payload);
  }
}

auth.guard.ts

import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard, IAuthGuard } from '@nestjs/passport';
import { User } from '../entities/user.entity';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') implements IAuthGuard {
  public handleRequest(err: unknown, user: User): any {
    return user;
  }

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    await super.canActivate(context);

    const { user } = context.switchToHttp().getRequest();

    return user ? true : false;
  }
}

auth.helper.ts:

import {
  Injectable,
  HttpException,
  HttpStatus,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { User } from '../entities/user.entity';

@Injectable()
export class AuthHelper {
  @InjectRepository(User)
  private readonly repository: Repository<User>;

  private readonly jwt: JwtService;

  constructor(jwt: JwtService) {
    this.jwt = jwt;
  }

  public async decode(token: string): Promise<unknown> {
    return this.jwt.decode(token, null);
  }

  public async validateUser(decoded: any): Promise<User> {
    return this.repository.findOne(decoded.id);
  }

  public generateToken(user: User): string {
    return this.jwt.sign({
      id: user.userId,
      username: user.username,
    });
  }

  public isPasswordValid(password: string, userPassword: string): boolean {
    return bcrypt.compareSync(password, userPassword);
  }

  public encodePassword(password: string): string {
    const salt: string = bcrypt.genSaltSync(10);

    return bcrypt.hashSync(password, salt);
  }
  private async validate(token: string): Promise<boolean | never> {
    const decoded: unknown = this.jwt.verify(token);

    if (!decoded) {
      throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
    }

    const user: User = await this.validateUser(decoded);

    if (!user) {
      throw new UnauthorizedException();
    }

    return true;
  }
}

I get some error like this:

[Nest] 18360  - 05/10/2022, 18:14:42   ERROR [ExceptionHandler] Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument ConfigService at index [0] is available in the JwtModule context.

Potential solutions:
- If ConfigService is a provider, is it part of the current JwtModule?
- If ConfigService is exported from a separate @Module, is that module imported within JwtModule?
  @Module({
    imports: [ /* the Module containing ConfigService */ ]
  })

Error: Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument ConfigService at index [0] is available in the JwtModule context.

Potential solutions:
- If ConfigService is a provider, is it part of the current JwtModule?
- If ConfigService is exported from a separate @Module, is that module imported within JwtModule?
  @Module({
    imports: [ /* the Module containing ConfigService */ ]
  })

I have already tested and implemented these solutions:

Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86

2 Answers2

1

Here is the solution. I hope it would help.

In your auth strategy add this:

  constructor() {
    super({
     jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
     secretOrKey: 'KhubSecret',
     ignoreExpiration: true,
   });
  }

instead of:

  constructor(@Inject(ConfigService) config: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'KhubSecret',
      ignoreExpiration: true,
     });
    }

In Short, Remove

@Inject(ConfigService) config: ConfigService

from your constructor

Sparko Sol
  • 651
  • 1
  • 9
0

From the immediate error you've provided, I'll assume that your ConfigModule is not global. This means that you'll need to import the ConfigModule in the JwtModule.registerAsync() call (in the imports array). Another option would be to make your ConfigModule global (@nestjs/config has an option for this) so that you already have access to ConfigService.

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147