24

Let's say I have my module defined as below:

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      // Use ConfigService here
      secretOrPrivateKey: 'secretKey',
      signOptions: {
        expiresIn: 3600,
      },
    }),
    PrismaModule,
  ],
  providers: [AuthResolver, AuthService, JwtStrategy],
})
export class AuthModule {}

Now how can I get the secretKey from the ConfigService in here?

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
THpubs
  • 7,804
  • 16
  • 68
  • 143

2 Answers2

62

You have to use registerAsync, so you can inject your ConfigService. With it, you can import modules, inject providers and then use those providers in a factory function that returns the configuration object:

JwtModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    secretOrPrivateKey: configService.getString('SECRET_KEY'),
    signOptions: {
        expiresIn: 3600,
    },
  }),
  inject: [ConfigService],
}),

For more information, see the async options docs.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • 3
    Also, you don't need to import ConfigModule if you have already imported it globally in your app module .i.e ConfigModule.forRoot({ isGlobal: true, expandVariables: true, }), – Segun Kess Apr 20 '20 at 04:18
  • 1
    @SegunKess that's a pretty nice hint! – Remi Guan Jul 13 '21 at 08:55
  • 1
    wiwth "expiresIn: 3600", I wasn't able to authorize. Adding the "s" to get "expiresIn: 3600s" solved the problem - such small one ... – Stefan Klocke Apr 03 '23 at 20:40
  • From the docs: "A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms")." As this is quite confusing, I'd always prefer the expressive variant as string including the time unit. – Kim Kern Apr 04 '23 at 15:22
-1

Or there is another solution, create an JwtStrategy class, something like this:

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(private readonly authService: AuthService) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: config.session.secret,
            issuer: config.uuid,
            audience: config.session.domain
        });
    }

    async validate(payload: JwtPayload) {
        const user = await this.authService.validateUser(payload);
        if (!user) {
            throw new UnauthorizedException();
        }
        return user;
    }
}

There you are able to pass ConfigService as a parameter to the constructor, but I'm using config just from plain file.

Then, don't forget to place it in array of providers in module.

Regards.

cojack
  • 2,444
  • 1
  • 17
  • 18
  • you can't pass the `ConfigService` here, because the use of `this` is not allowed in `super()` – Theo Sep 29 '20 at 13:47
  • que? [10 more to go] – cojack Sep 30 '20 at 09:52
  • if you pass the `ConfigService` in the constructor, you can't access it in the `super()` function. https://stackoverflow.com/questions/51896505/this-is-not-allowed-before-superclass-constructor-invocation – Theo Sep 30 '20 at 10:05
  • and why would you need to access it by this there? – cojack Sep 30 '20 at 12:36
  • i was just replying to what you said... You're using a static config file. I'm just telling it won't work with the ConfigService, that's all – Theo Sep 30 '20 at 15:11
  • But even if you pass the ConfigService here you are able to use it without this, just by name of the argument. – cojack Oct 07 '20 at 09:27
  • It worked for me and I was able to access the configService from the constructor: constructor(private readonly configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('jwt.secret'), }); } – Yousi Jun 05 '23 at 09:02