user2623505
user2623505

Reputation: 349

Initialize Guard with value

Is it possible to initialize guard with a specifig value ? For example the current example will not work:

@Module({
  imports: [
    CoreModule,
  ],
  providers: [
    {
      provide: AuthGuard, // while using APP_GUARD works
      useFactory: (configService: ConfigService) => {
        return new AuthGuard(configService.get('some_key'));
      },
      inject: [ConfigService],
    },
  ],
})

While using APP_GUARD for provide will initialise the guard with config value. So it works only for global scope, but not for @UseGuards(AuthGuard)

Upvotes: 4

Views: 2760

Answers (3)

Paul Lucius Valor
Paul Lucius Valor

Reputation: 101

Let's say you want your specific guard instance to perform differently depending on some input, basically be able to configure it. There is no option to consume this config from constructor(). Factory way might look like a bit bulky solution. But you're still able to utilise static methods to achieve wanted behaviour.

Example:

@Injectable()
class SomeController {
  @Get()
  @UseGuard(AuthGuard) // but how to pass smth inside AuthGuard?
  public async doSomething() {}
}

Solution:

// [auth.guard.ts] file
import { UnauthorizedException, Injectable } from '@nestjs/common';
import type { CanActivate, ExecutionContext } from '@nestjs/common';
import type { GuardOptions, PatchedRequest } from './auth.types';


export interface GuardOptions {
  allowAnonymous?: boolean,
  allowExpired?: boolean,
}

@Injectable()
export class AuthGuard
implements CanActivate {
  public options: GuardOptions = {};
  public canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> {
    // Guard logic 
    return true;
  }

  static configure(options: GuardOptions) {
    const instance = new AuthGuard;
    instance.options = options;
    return instance;
  }
}


// [someEntity.controller.ts] file
// imports...

@Injectable()
class SomeController {
  @Get()
  @UseGuard(AuthGuard.configure({ allowExpired: true })) // voila
  public async doSomething() {}
}

Enjoy! Glory to Ukraine!

Upvotes: 2

Kim Kern
Kim Kern

Reputation: 60347

This doesn't work because guards are not registered as providers in a module. They get directly instantiated by the framework.

You can either use dependency injection in the guard:

@Injectable()
export class MyAuthGuard {
  constructor(private readonly configService: ConfigService) {
    // use the configService here
  }
}

and

@UseGuards(MyAuthGuard)

or instantiate the guard yourself:

@UseGuards(new AuthGuard(configService.get('some_key')))

In the special case of the AuthGuard, you can set a defaultStrategy in the PassportModule. Then you can just use @UseGuards(AuthGuard())

PassportModule.register({ defaultStrategy: 'jwt'}) 

or async:

PassportModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({ defaultStrategy: configService.authStrategy}),
  inject: [ConfigService],
}) 

Upvotes: 4

Igor
Igor

Reputation: 83

I would try ht less verbose approach and inject ConfigService directly into the AuthGuard in such a manner:

@Module({
  imports: [
    CoreModule,
  ],
  providers: [
    AuthGuard,
  ],
  exports: [
    AuthGuard,
  ],
})
@Injectable()
export default class AuthGuard {
  constructor (protected readonly config: ConfigService) {
  }
  /*
  ...
  */
}

Upvotes: 0

Related Questions