Nest: Add exclude feature for useGlobalGuards

Created on 12 Aug 2018  路  7Comments  路  Source: nestjs/nest

Similiar to the exclude feature for middleware I am hereby requesting an exclude feature for the useGlobalGuards method. I believe one can use the same arguments as used in the middleware exclude feature request issue (#790), to argue why this feature is helpful.

Use case:

While I am using a generally JWT protected API, I'd like to have an unprotected health and metrics endpoint so that it can be scraped by Kubernetes and Prometheus. Therefore I would like to exclude these two controllers.

Most helpful comment

What we ended up doing was to create a global guard (in this case the AuthGuard) which looks like this:

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";

@Injectable()
export class AuthGuard implements CanActivate {
    public constructor( private readonly reflector: Reflector ) {
    }

    public canActivate( context: ExecutionContext ): boolean {
        const isPublic = this.reflector.get<boolean>( "isPublic", context.getHandler() );

        if ( isPublic ) {
            return true;
        }

        // Make sure to check the authorization, for now, just return false to have a difference between public routes.
        return false;
    }
}

This guard is then added globally to all the routes using the following in main.ts:

const reflector = app.get( Reflector );
app.useGlobalGuards( new AuthGuard( reflector ) );

Exclusions to this guard can be made by using a decorator. We named it Public() which looks like this:

import { SetMetadata } from "@nestjs/common";

export const Public = () => SetMetadata( "isPublic", true );

Now authentication is required for all the routes and exclusions for a specific route or class can be made by adding the @Public() decorator. For example:

@Public()
@Get()
public getHello(): string {
   return this.appService.getHello();
}

Of course you have to add your own authentication to the auth.guard.ts to make sure that it returns true whenever someone is authorised to visit the route (probably if req.user is set...).

All 7 comments

Honestly, it's almost impossible to port this feature to Nest. The execution context, global injectables like guards, interceptors, they are held separately, regardless of application's type. We cannot differentiate them by path, that would require a lot of design changes.

@kamilmysliwiec Alright, do you have a suggestion to handle my the problem in the described use case with NestJS though?

I'm guessing that the only available solutions are: a) use different binding scope (one by one tie guard to the controllers/methods), b) add if statement directly in the guard (for example, extend AuthGuard pick request from ExecutionContext and compare request path)

@weeco how did you end up accomplishing this? Any recommendations?

I am also looking for a smart solution for this situation. :slightly_frowning_face:

What we ended up doing was to create a global guard (in this case the AuthGuard) which looks like this:

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";

@Injectable()
export class AuthGuard implements CanActivate {
    public constructor( private readonly reflector: Reflector ) {
    }

    public canActivate( context: ExecutionContext ): boolean {
        const isPublic = this.reflector.get<boolean>( "isPublic", context.getHandler() );

        if ( isPublic ) {
            return true;
        }

        // Make sure to check the authorization, for now, just return false to have a difference between public routes.
        return false;
    }
}

This guard is then added globally to all the routes using the following in main.ts:

const reflector = app.get( Reflector );
app.useGlobalGuards( new AuthGuard( reflector ) );

Exclusions to this guard can be made by using a decorator. We named it Public() which looks like this:

import { SetMetadata } from "@nestjs/common";

export const Public = () => SetMetadata( "isPublic", true );

Now authentication is required for all the routes and exclusions for a specific route or class can be made by adding the @Public() decorator. For example:

@Public()
@Get()
public getHello(): string {
   return this.appService.getHello();
}

Of course you have to add your own authentication to the auth.guard.ts to make sure that it returns true whenever someone is authorised to visit the route (probably if req.user is set...).

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rafal-rudnicki picture rafal-rudnicki  路  3Comments

thohoh picture thohoh  路  3Comments

yanshuf0 picture yanshuf0  路  3Comments

mishelashala picture mishelashala  路  3Comments

breitsmiley picture breitsmiley  路  3Comments