Andrew
Andrew

Reputation: 9

Guard in NestJs isn't work when i give it globally

I'm using an AuthGuard component globally in my NestJS application to protect routes, while allowing some routes to be public using a Public decorator. However, the guard isn't functioning as expected, because I can send requests to any route, even those without the Public decorator, without facing any restrictions.

authGuard

import { JwtService } from '@nestjs/jwt';
import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './utils';
import { jwtConstants } from './constants';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,
    private reflector: Reflector,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException();
    }
    try {
      request['user'] = await this.jwtService.verifyAsync(token, {
        secret: jwtConstants.secret,
      });
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

Public decorator

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

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

app.module

providers: [
  AppService,
  {
    provide: APP_GUARD,
    useClass: AuthGuard, 
  },
],

controller expample

@Post()
@UseInterceptors(
  FileInterceptor('image', {
    storage: diskStorage({
      destination: './uploads/news',
      filename: fileName,
    }),
  }),
)

In the controller i didnt put Public decorator but its working

I tryed to put authguard itself to the routes which i want to protect, but this method didnt work too

Upvotes: 0

Views: 66

Answers (2)

Nishan Raut
Nishan Raut

Reputation: 38

My suggestion is you to wrap your AuthGuard to Decorator

export function AuthUserGuard(): MethodDecorator {
  return applyDecorators(UseGuards(AuthGuard));
}

Then you can use this guard in you specific APIs where you need. For example

  @AuthUserGuard()
  @Post('/create')
  async create(@Body() customerData: CustomerCreateDto) {
    try {
     //Your Business Logic
    } catch (error) {
      throw error;
    }
  }

Only use this decorator where you need a valid logged in user.

Make Sure to remove

const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
  context.getHandler(),
  context.getClass(),
]);
if (isPublic) {
  return true;
}

Upvotes: 0

Manuel Duarte
Manuel Duarte

Reputation: 1075

You need to define this guard as a global guard in your main.ts

const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());

By the way, you don't need to throw an error in your canActivate method from AuthGuard class, you can return a boolean value and Nest can handle the rest since your guard is implementing CanActivate interface.

Upvotes: 0

Related Questions