NestJS Guard fails, action still executes

38 Views Asked by At

I've inherited a problem in a NestJS project where a guard has been setup to check for a JWT but the guard doesn't work correctly and I'm really struggling to figure out why. From what I can see in my logs the guard is failing, but instead of throwing an error and preventing the action on the controller from executing, its not doing anything and letting the action execute.

the guard in question is as follows:

import {
  ExecutionContext,
  Injectable,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY } from './decorators/public.decorator';

@Injectable()
/**
 * An API guard used to indicate if the decorated API requires authenticaiton.  If the API's class (or method) is decorated with @Public, then authentication is not required.
 */
export class JwtAuthGuard extends AuthGuard('jwt') {

  private readonly logger = new Logger(JwtAuthGuard.name);

  constructor(private reflector: Reflector) {
    super();
  } 
  
  // returns true if the api is @Public.  Otherwise, the api will require a valid token as per the Passport strategy jwtauth.stratagy
  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    
    if (isPublic) {
      return true;
    } else {
      return super.canActivate(context);
    }
  } 
  
  handleRequest(err, user, info) {
    if (err || !user) {
      this.logger.error(`JWT is not Valid.  Err: ${err}. - User ${user}. - Info. ${info}`);
      throw err || new UnauthorizedException();
    }
    return user;
  }
}

This guard is then applied to the root of the controller as follows:

@UseGuards(JwtRoleGuard)
@ApiTags("complaint")
@Controller({ path: 'complaint', version: '1'})
export class ComplaintController {
  constructor(private readonly complaintService: ComplaintService) {}

  @Get(':id')
  @Roles(Role.COS_OFFICER)
  findOne(@Param('id') id: string) {
    return this.complaintService.findOne(id);
  }

  @Patch(':id')
  @Roles(Role.COS_OFFICER)
  update(@Param('id') id: string, @Body() updateComplaintDto: UpdateComplaintDto) {
    return this.complaintService.update(id, updateComplaintDto);
  }
}

When either of these two actions are used, I end up with the following in my logs:

2023-10-02 17:25:28 [Backend - c1c7ed] error    2023-10-03 12:25:28.331 [JwtAuthGuard]  JWT is not Valid.  Err: null. - User false. - Info. TokenExpiredError: jwt expired - { stack: [ null ] }
2023-10-02 17:25:28 [Backend - c1c7ed] info     2023-10-03 12:25:28.335 [HTTP]  PATCH /v1/hwcr-complaint/a5d359d1-1105-4b8e-b599-c66f3c948a56 401 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.81 - {}
2023-10-02 17:25:28 [Backend - c1c7ed] debug    2023-10-03 12:25:28.397 [JwtRoleGuard]  Guarded Roles: COS Officer - {}
2023-10-02 17:25:28 [Backend - c1c7ed] debug    2023-10-03 12:25:28.397 [JwtRoleGuard]  User authorization verified - {}

I'm at a loss as to why this is happening as my understanding is that when the guard fails, it shouldn't even allow the action to execute, but it is.

0

There are 0 best solutions below