6

I'm trying to get access to the jwt payload in a route that is protected by an AuthGuard.

I'm using passport-jwt and the token payload is the email of the user.

I could achieve this by runing the code bellow:

import {
    Controller,
    Headers,
    Post,
    UseGuards,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AuthGuard } from '@nestjs/passport';

@Post()
@UseGuards(AuthGuard())
async create(@Headers() headers: any) {
    Logger.log(this.jwtService.decode(headers.authorization.split(' ')[1]));
}

I want to know if there's a better way to do it?

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
TheAngularGuy
  • 939
  • 8
  • 13
  • 1
    Side note: better create an instance of `Logger` than using it statically. See this answer: https://stackoverflow.com/a/52907695/4694994 – Kim Kern Apr 08 '19 at 14:32

1 Answers1

12

Your JwtStrategy has a validate method. Here you have access to the JwtPayload. The return value of this method will be attached to the request (by default under the property user). So you can return whatever you need from the payload here:

async validate(payload: JwtPayload) {
  // You can fetch additional information if needed 
  const user = await this.userService.findUser(payload);
  if (!user) {
    throw new UnauthorizedException();
  }
  return {user, email: payload.email};
}

And then access it in you controller by injecting the request:

@Post()
@UseGuards(AuthGuard())
async create(@Req() request) {
    Logger.log(req.user.email);
}

You can make this more convenient by creating a custom decorator:

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

export const User = createParamDecorator((data, req) => {
  return req.user;
});

and then inject @User instead of @Req.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • I don't think you need to call `validateUser(payload)` in your `validate` function. your payload is already valid by this point, and decoded by `Passport`. You should simply return the new object `return {user, email: payload.email};` – Genu Dec 16 '19 at 05:46
  • It ist just as in the docs: "A "verify callback", which is where you tell Passport how to interact with your user store (where you manage user accounts). Here, you verify whether a user exists (and/or create a new user), and whether their credentials are valid. The Passport library expects this callback to return a full user if the validation succeeds, or a null if it fails (failure is defined as either the user is not found, or, in the case of passport-local, the password does not match)." https://docs.nestjs.com/techniques/authentication – Kim Kern Dec 16 '19 at 09:25
  • For the other strategies, yes, but for JWT its not necessary because your token was signed and verified. See the later section of that documentation on why you don't have to validate the user here once again. – Genu Dec 17 '19 at 07:33
  • Yes, this is right, you do not need to validate the token itself again. However, you might want to use the hook to fetch additional data to attach it to the request. Or if you have a long-lived token, (which may not be recommended for most cases), you might want to check if the user still exists, was blacklisted etc. I've edited the answer to include your point. – Kim Kern Dec 17 '19 at 08:26
  • The parameter decorator should be implemented using execution context: `import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const User = createParamDecorator( (data: any, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user; }, )` – Hamidreza Vakilian Dec 26 '21 at 21:28