0

I have a controller with a route that I want to protect with Guards. I have these three gards: IsAuthentifiedGuard, HasRoleGuard and IsSafeGuard and I want at least one of these two conditions to be valid:

  • IsAuthentifiedGuard and HasRoleGuard are OK
  • IsSafeGuard is OK

For example, if the user is authentified AND has an enough role, it should be OK but if not but the call is "safe" I also want it to be OK.

My OrGuard:

@Injectable()
export class OrGuard implements CanActivate {
  constructor(private readonly orGuards: CanActivate[][]) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const orGuardsPromises = await Promise.all(
      this.orGuards.map(async andGuards => {
        const andGuardPromises = await Promise.all(
          andGuards.map(async guard => {
            return guard.canActivate(context)
          }),
        )
        return andGuardPromises.every(canActivate => canActivate === true)
      }),
    )

    return orGuardsPromises.some(canActivate => canActivate === true)
  }
}

And my controller:

@Controller('my_route')
export class MyController {
  @Post('/')
  @UseGuards(new OrGuard([[new IsAuthentifiedGuard(), new HasRoleGuard()], [new IsSafeGuard()]]))
  async myRoute() {}
}

But I've a problem with the guards that require dependencies. For example if HasRoleGuard requires a ConfigService in constructor, this doesn't work.

An other way would be to use my or guard as following:

@UseGuards(OrGuard)
@SetMetadata('orGuards', [[IsAuthentifiedGuard, HasRoleGuard], [IsSafeGuard]])

But in my guard, I don't succeed to instanciate these guards from their class name with all the dependencies they need.

What can you suggest?

1 Answer 1

1

What you're looking to do is possible with @nest-lab/or-guard. You'll need to register your guards as providers (just adding them to the providers array is fine) and you'll want to create a custom provider for the AndGuard so that it can later be referenced by the OrGuard. Something like this should do for you:

@Module({
  providers: [
    IsAuthentifiedGuard,
    HasRoleGuard,
    IsSafeGuard,
    {
      provide: 'AndGuard',
      useClass: AndGuard([IsAuthenticatedGuard, HasRoleGuard]),
    },
    ...
  ]
})

And then you can use @UseGuards(OrGuard(['AndGuard', IsSafeGuard])) and it should work just fine. You can view the tests for some more inspiration on how it works if you'd like

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.