NestJS: How to get User from the request in a GraphQL custom Guard which extends from JWT AuthGuard

NestJS: How to get User from the request in a GraphQL custom Guard which extends from JWT AuthGuard


4

I use Passport and GraphQL and if I do my own custom guard to get user’s roles it just doesn’t work. I don’t have access to the user from the request in a Guard, is that intentional? I think it should be part of the request (I’m totally new to NestJS), so why I can’t get it? I would like to get the user’s roles directly from the user object and only allow users with Admin for specific routes like the full list of users.

This is my GraphQL guard, the console log always return undefined

@Injectable()
export class GraphqlPassportAuthGuard extends AuthGuard('jwt') {
  roles: string[];

  constructor(roles?: string | string[]) {
    super();
    this.roles = Array.isArray(roles) ? roles : [roles];
  }

  canActivate(context: ExecutionContext): boolean {
    const ctx = GqlExecutionContext.create(context);
    const req = ctx.getContext().req;
    console.log('graphql guard user', req && req.user)
    return true;
  }

  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    const req = ctx.getContext().req;
    console.log('graphql guard user', req && req.user)
    return req;
  }
}

and I try using it with following code

export const CurrentUser = createParamDecorator(
  (data, [root, args, ctx, info]) => {
    return ctx.req.user;
  },
);

@Resolver()
export class UsersResolver {
  constructor() { } 

  @Query(() => UserDto)
  @UseGuards(GraphqlPassportAuthGuard)
  whoAmI(@CurrentUser() user: User) {
    return user;
  }
}

app.module.ts

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: 'schema.gql',
      context: ({ req }) => ({ req }),
    }),
    // ...
});

I can get the user from the @CurrentUser() with a Custom Decorator but I can’t get the user object inside the guard. I’m trying to get the user from the request, but is that possible in a Guard or not? I tried so many things, I don’t know what to do anymore.

I also tried to do another Roles Guard with different code and again I’m trying to get the user from the request and I can’t get the user from the request.

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) { }

  canActivate(context: ExecutionContext): boolean {
    const handler = context.getHandler();
    const http = context.switchToHttp();
    const request = context.switchToHttp().getRequest();
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    console.log('rolesGuard', roles, request && request.user)
    if (!roles || !request || !request.user) {
      return true;
    }
    const user = request && request.user;
    const hasRole = () =>
      user.roles.some(role => !!roles.find(item => item === role));

    return user && user.roles && hasRole();
  }
}

Share
Improve this question

1 Answer
1

Reset to default


5

On your GraphqlPassportAuthGuard.canActivate() you should call await super.canActivate(context), and change the return value type for Promise<boolean>, this is where the @nestjs/passport AuthGuard put the user in the Request object.

If, for some reason, you don’t want the guard to throw when the user is not authenticated, you have to override also the AuthGuard.handleRequest(err, user, info, context)

Share
Improve this answer

2

  • Sorry for the delay, I just tried this and I still have the same result undefined, am I supposed to still calli the same 2 lines after the super.canActivate? That 2nd line ctx.getContext().req still doesn't have the user that I'm looking for. Would you mind providing the full code of the canActive method? Am I suppose to delete something or call something else? I'm totally new to NestJS, I'm a little confused. My main goal is the get the user's role and block the user if he doesn't have necessary role

    – ghiscoding

    Dec 7, 2019 at 21:27


  • 1

    You should await for super.canActivate since it returns a Promise, sorry I didn't mention in my original answer. So make your definition of canActivate async and call await super.canActivate(context). Remember also to change the return type to Promise<boolean>

    – Ghero

    Dec 8, 2019 at 21:35




Not the answer you're looking for? Browse other questions tagged

or ask your own question.

Leave a Reply

Your email address will not be published. Required fields are marked *