graphqlnestjsnestjs-passportnestjs-jwt

Authorization in Nestjs using graphql


I have started to learn Nestjs, express and graphql. I encountered a problem while trying to authorize access of user authenticated using jwt token. I followed the tutorial for authentication on the Nestjs website. I am able to get the current user, but when I try implementing the basic role base access control, I am unable to access the current user in the canActivate Method. I think it is because the Roles Guard is executed before the Graphql Guard.

I will post the codes here

gql-auth.guard.ts

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

export class GqlAuthGuard extends AuthGuard("jwt") {
    getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);
        console.log("gql simple context: ", context);
        console.log("gqlContext: ", ctx.getContext());
        return ctx.getContext().req;
    }
}

roles.guard.ts

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { GqlExecutionContext } from "@nestjs/graphql";

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

    canActivate(context: ExecutionContext) {
        const roles = this.reflector.get<string[]>("roles", context.getHandler());
        const ctx = GqlExecutionContext.create(context);
        console.log("roles: ", roles);
        console.log("context: ", context.switchToHttp().getRequest());
        console.log("gqlContext: ", ctx.getContext().req);

        return true;
    }
}

jwt.strategy.ts

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { jwtConstants } from "../constants";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor() {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: jwtConstants.secret,
        });
    }

    validate(payload: any) {
        console.log("payload: ", payload);

        return payload;
    }
}

resolver

@UseGuards(GqlAuthGuard)
@Roles("ADMIN")
@UseGuards(RolesGuard)
@Query((returns) => [Specialty], { nullable: "itemsAndList", name: "specialties" })
async getSpecialties(@Args() params: FindManySpecialtyArgs, @Info() info: GraphQLResolveInfo) {
    const select = new PrismaSelect(info).value;
    params = { ...params, ...select };
    return this.prismaService.specialty.findMany(params);
}

Has anyone successfully implemented this before ?


Solution

  • You should use the two guards in the same @UseGuards() decorator. Like @UseGuards(GqlAuthGuard, RolesGuard). Nest will run these sequentially without a problem.