how to implement user guards in nestjs graphql
Asked Answered
G

3

14

I'm trying to get the current user but in the resolver I get undefined, in the jwt strategy I get the user object using the token but in the resolver the user is undefined

auth guard

import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthenticationError } from 'apollo-server-core';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

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

    canActivate(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
        const { req } = ctx.getContext();

        return super.canActivate(
            new ExecutionContextHost([req]),
        );
    }

    handleRequest(err: any, user: any) {
        if (err || !user) {
            throw err || new AuthenticationError('GqlAuthGuard');
        }
        return user;
    }

}

user decorator

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

export const CurrentUser = createParamDecorator(
    (data, req) =>  req.user )
;

app module

@Module({
  imports: [
      PassportModule.register({ defaultStrategy: 'jwt' }),
      JwtModule.register({
          signOptions: {
              expiresIn: 3600,
          },
      }),
      SharedModule,
      AuthModule,
      GraphQLModule.forRoot({
          autoSchemaFile: 'schema.gql',
          context: ({ req }) => ({ req })
      }),
      MongooseModule.forRoot(process.env.MONGO_URI,
        {
          useNewUrlParser: true ,
          useUnifiedTopology: true
        }),
    // RewardsModule,
    OrdersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

resolver

import {User} from "src/types/user";
import {GqlAuthGuard} from "../guards/graphql.auth.guard";

@Resolver()
export class OrdersResolver {
    constructor(
        private orderService: OrdersService
    ) {
    }

    @Query(returns => [Order])
    @UseGuards(GqlAuthGuard)
    listOrders(@CurrentUser() user: User): Promise<Order> {
        console.log(user)
        return this.orderService.listOrdersByUser(user.id);
    }

}

I also tried to implement the solution explained here NestJS Get current user in GraphQL resolver authenticated with JWT and still, I got the same error

Gaudy answered 26/4, 2020 at 17:34 Comment(2)
Quick question: what's your Nest version? There was a change to the createParamDecorator function between v6 and v7.Marxmarxian
These are the nestjs versions "@nestjs/common": "^7.0.0", "@nestjs/core": "^7.0.0", "@nestjs/graphql": "^7.3.4", "@nestjs/jwt": "^7.0.0", "@nestjs/mongoose": "^6.4.0", "@nestjs/passport": "^7.0.0", "@nestjs/platform-express": "^7.0.0", "@nestjs/swagger": "^4.5.1",Gaudy
M
10

With Nest v7, the createParamDecorator function got an update. Now, instead of using

export const CurrentUser = createParamDecorator(
    (data, req) =>  req.user )
;

you should instead be using something like this:

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const gqlCtx = GqlExecutionContext.create(ctx);
    const request = gqlCtx.getContext().req;
    return request.user;
  },
);

For this to work, you also need

context: ({req}) => ({req})

in your GraphqlModule configuration.

Marxmarxian answered 27/4, 2020 at 16:44 Comment(0)
T
4

This is what I'm using for GraphqlJwtAuthGuard based on documentaion:

@Injectable()
export class GqlJwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(ctx: ExecutionContext) {
    const context = GqlExecutionContext.create(ctx);
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) {
      return true;
    }
    const { req } = context.getContext();
    return super.canActivate(new ExecutionContextHost([req])); // NOTE
  }
}
Temperate answered 28/1, 2022 at 17:12 Comment(0)
C
3

this is complete code:

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

  canActivate(context: ExecutionContext): boolean {
    const ctx = GqlExecutionContext.create(context);

    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRoles) {
      return true;
    }

    const { user } = ctx.getContext().req;
    return requiredRoles.some((role) => user.role?.includes(role));
  }
}
Cincinnatus answered 26/12, 2021 at 20:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.