2
votes

Does anybody know where I can see the full code of canActivate method in AuthGuard('jwt')? I realized that canActivate method calls JwtStrategy validate method by using console.log() like this:

// jwt.strategy.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly configService: ConfigService,
    private readonly usersService: UsersService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: true,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    });
  }

  async validate(payload: any) {
    try {
      const user = await this.usersService.getUserById(payload.id);
      // console.log is here
      console.log(user);
      return user;
    } catch (e) {
      console.log(e);
      return null;
    }
  }
}

If I use the original canActivate method, console.log is called. I thought that JwtStrategy is a middleware so the validate method is called whenever there is a request. However, when I try to override canActivate method to add authorization, console.log in JwtStrategy validate method is not called:

// jwt-auth.guard.ts

import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }

  canActivate(context: ExecutionContext): boolean {
    try {
      // Override: handle authorization
      // return true or false
      // Should JwtStrategy.validate(something) be called here?
    } catch (e) {
      console.log(e);
      return false;
    }
  }
}

Then I tried to find the original code of AuthGuard('jwt') in order to understand its logic, but I was not able to. Any help would be appreciated, thanks!

1

1 Answers

1
votes

Okay, so this is gonna be a very fun deep dive into this. Buckle up.

Middleware as express methods do still exist in NestJS; that said, this is not your noraml middleware in the sense of Express middleware. As you'v mentioned AuthGuard()#canActivate() ends up calling the appropriate PassportStrategy. These strategies get registered here specifically on lines 40-41 where passport.use() gets called. This registers the passport strategy class's validate method to be used for passport.veryfiy(). Most of the under the hood logic is very abstract and the context while reading it can be lost, so take your time and understand the concepts of classes, mixins (functions that return classes), and inheritance.

Line 51 of AuthGuard is where the passportfn originally gets created, and in this passportFn passport.authenticate gets called (which calls passport.verify under its hood) (reading through Passport's code is even more confusing, so I'll let you run that when you want).

If you want to add some extra logic to your canActivate() method you can end up calling super.canActivate(context) to call the original canActivate() method that ends up calling passport.authetnicate() and thus <Strategy>#validate. That could look something like

@Injectable()
export class CustomAuthGuard extends AuthGuard('jwt') {

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // custom logic can go here
    const parentCanActivate = (await super.canActivate(context)) as boolean; // this is neccssary due to possibly returning `boolean | Promise<boolean> | Observable<boolean>
    // custom logic goes here too
    return parentCanActivate && customCondition;
  }
}