0

I am new to Nestjs and I am using guards, strategies and passport for authentification. I don't understand what's going on under the hood.

I have a guard for a refreshToken mutation:

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

export class RtGuard extends AuthGuard('jwt-refresh') {
  constructor() {
    super();
  }
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    console.log('REFRESH');
    return ctx.getContext().req;
  }
}

What does this guard exactly do? Somehow it calls my strategy right? But it only does it, if I provide a correct refreshToken.

This is my Strategy:

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, ForbiddenException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtPayloadWithRt } from '../types/jwtPayloadWithRt.type';
import { JwtPayload } from 'src/auth/types/jwtPayload.type';
import { Request } from 'express';

@Injectable()
export class RefreshTokenStrategy extends PassportStrategy(
  Strategy,
  'jwt-refresh',
) {
  constructor(config: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: config.get<string>('REFRESH_TOKEN_SECRET'),
      passReqToCallback: true,
    });
  }

  validate(req: Request, payload: JwtPayload): JwtPayloadWithRt {
    const refreshToken = req.get('authorization')?.replace('Bearer', '').trim();

    if (!refreshToken) throw new ForbiddenException('Refresh token malformed');

    return {
      ...payload,
      refreshToken,
    };
  }
}

How is the guard able to decide whether my refresh token is the one in my database and if so, then it calls my strategy?

If I use a wrong refreshToken, not the one I got when I signed in, I get this error: error when providing wrong refreshToken

when providing the correct key, I get this: response after providing correct refreshToken

Using console.log, I can see that my strategy is not called, whenever the refreshtoken is invalid. How does the validation work exactly? How do guard and strategy work together under the hood?

Thanks a lot for help!

69JonDoe69
  • 41
  • 1
  • 6

1 Answers1

0

Named strategies

When implementing a strategy, you can provide a name for it by passing a second argument to the PassportStrategy function. If you don't do this, each strategy will have a default name (e.g., 'jwt' for jwt-strategy):

export class JwtStrategy extends PassportStrategy (Strategy, 'myjwt')

Default name !== class name. The default name is imposed by the strategy you use.

For example, the default strategy for passport-jwt https://github.com/mikenicholson/passport-jwt is jwt

Source: https://github.com/nestjs/nest/issues/4753#issuecomment-625675342


About how the guard is able to decide whether the token is in the database:

It doesn't. It just verifies that is valid, it is done using the secret key, which has to be the same that you signed the token with in the beggining. If it isn't valid it will throw a ForbiddenException thus the application never reaches the console.log('REFRESH') part of your code.

You could validate that it is in the db by your self, that's what the validate() method could be used for.

A quote from the nestjs docs:

It's also worth pointing out that this approach leaves us room ('hooks' as it were) to inject other business logic into the process. For example, we could do a database lookup in our validate() method to extract more information about the user, resulting in a more enriched user object being available in our Request. This is also the place we may decide to do further token validation, such as looking up the userId in a list of revoked tokens, enabling us to perform token revocation. The model we've implemented here in our sample code is a fast, "stateless JWT" model, where each API call is immediately authorized based on the presence of a valid JWT, and a small bit of information about the requester (its userId and username) is available in our Request pipeline.

Source: https://docs.nestjs.com/security/authentication#implementing-passport-jwt

validate(req: Request, payload: JwtPayload): JwtPayloadWithRt {
    const refreshToken = req.get('authorization')?.replace('Bearer', '').trim();

    if (!refreshToken) throw new ForbiddenException('Refresh token malformed');

    /*
    Perform database checks in this part of your code
    */

    //Whatever you return here gets attached to the Request object as `req.user`, 
    //you can change it to whatever you want
    return {
      ...payload,
      refreshToken,
    };
  }
Manuvo
  • 748
  • 5
  • 15