7

I want to create a NestJs app and want to have a middleware validating the token in the request object and a authentication guard validating the user in the token payload.

By splitting this I was hoping to have a clean separation. First my middleware

@Injectable()
export class TokenMiddleware implements NestMiddleware {
  use(req: any, res: Response, next: NextFunction) {
    try {
      const headers: IncomingHttpHeaders = req.headers;
      const authorization: string = headers.authorization;
      const bearerToken: string[] = authorization.split(' ');
      const token: string = bearerToken[1];

      // !! Check if token was invalidated !!

      req.token = token;
      req.tokenPayload = verifyToken(token);
      next();
    } catch (error) {
      throw new UnauthorizedException();
    }
  }
}

It only validates the token and extends the request object with the encoded token and its payload. My auth guard

@Injectable()
export class AuthenticationGuard implements CanActivate {
  constructor(private readonly usersService: UsersService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request: any = context.switchToHttp().getRequest();

    try {
      const user: any = request.tokenPayload;

      if (!user) {
        throw new Error();
      }

      const findByIdDTO: FindByIdDTO = { id: user.id };
      const existingUser: UserRO = await this.usersService.findById(findByIdDTO);

      if (!existingUser) {
        throw new Error();
      }

      // attach the user to the request object?

      return true;
    } catch (error) {
      throw new UnauthorizedException();
    }
  }
}

This guard checks if the provided user in the tokenpayload is a valid one. If everything is fine, where should I attach the user to the request object? As far as I know the guard only checks if something is correct. But I don't want to keep all this logic in the token middleware. Where can I attach the database user to the request after finishing the validation in the auth guard?

2 Answers2

7

If you want to do something similar to Passport you could always attach the user to req.user, which is seen as a pretty standard ting in the Node.JS world.

Side question for you: any reason to not have two guards that function right after another? Have one guard for checking that the token is there and is indeed a valid token and one for validating the user on the token is indeed a valid on. That way you don't use a middleware (which is kind of included mostly for the sake of compatibility) and still have the separated logic.

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
1

Where can I attach the database user to the request after finishing the validation in the auth guard?

I believe that Guard, as you noticed, should validate if given user has the right to use given method.

Depending on your needs, you can go into different paths:

1) use passport and a strategy to do what you need (https://stackoverflow.com/a/57929429/4319037 I wrote a few words and lines about this already). Furthermore, it will already cover most of the code you have to extract the token.

@Injectable()
export class HttpStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super()
  }

  async validate(token: string) {
    const user = await this.authService.findUserByToken(token);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

2) use Interceptor on controller/method level to attach the user to given request (and throw if token is missing); your Guard will receive the user already, thus you can validate if the user has correct role/rights to execute the method.

Please let me know if I misunderstood what you want to achieve or need more details on particular way, thanks!

kamilg
  • 693
  • 4
  • 10