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)
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)
3 Answers
Reset to default 4This 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],
})
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/mon';
import type { CanActivate, ExecutionContext } from '@nestjs/mon';
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!
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) {
}
/*
...
*/
}