grahan
grahan

Reputation: 2398

NestJS Get current user in GraphQL resolver authenticated with JWT

I am currently implementing JWT authentication with Passport.js into a NestJS application.

In some of my GraphQL resolvers I need to access the currently authenticated user. I know that passport will attach the authenticated user to the request object (at least I hope that this is correct), but I do not know how to access the request object inside a resolver.

I followed the issue https://github.com/nestjs/nest/issues/1326 and the mentioned link https://github.com/ForetagInc/fullstack-boilerplate/tree/master/apps/api/src/app/auth inside the issue. I saw some code that uses @Res() res: Request as a method parameter in the GraphQL resolver methods, but I always get undefined for res.

These are the current implementations I have:

GQLAuth

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

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    const { req } = ctx.getContext();
    console.log(req);

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

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

Resolver that needs to access the current user

import { UseGuards, Req } from '@nestjs/common';
import { Resolver, Query, Args, Mutation, Context } from '@nestjs/graphql';
import { Request } from 'express';

import { UserService } from './user.service';
import { User } from './models/user.entity';
import { GqlAuthGuard } from '../auth/guards/gql-auth.guard';

@Resolver(of => User)
export class UserResolver {
  constructor(private userService: UserService) {}

  @Query(returns => User)
  @UseGuards(GqlAuthGuard)
  whoami(@Req() req: Request) {
    console.log(req);
    return this.userService.findByUsername('aw');
  }
}

JWT Strategy

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { JwtPayload } from './interfaces/jwt-payload.interface';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.SECRET,
    });
  }

  async validate(payload: JwtPayload) {
    const user = await this.authService.validateUser(payload);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

Authorization and creating JWT tokens works fine. GraphQL guard also works fine for methods that do not need to access the user. But for methods that need access to the currently authenticated user, I see no way of getting it.

Is there a way to accomplish something like this ?

Upvotes: 31

Views: 30825

Answers (6)

Alex Vasilev
Alex Vasilev

Reputation: 784

This worked for me

export const CurrentUser = createParamDecorator(
  (data, context: ExecutionContextHost) => {
    return GqlExecutionContext.create(context).getContext().req.user;
  },
);

Upvotes: 0

Alshoja
Alshoja

Reputation: 527

In order to use an AuthGuard with GraphQL, extend the built-in AuthGuard class and override the getRequest() method. Create a file called gql.guard.ts (Naming your wish)

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

To get the current authenticated user in your graphql resolver, you can define a @CurrentUser() decorator (create a file called user.decorator.graphql.ts)

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

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

To use above decorator in your resolver, be sure to include it as a parameter of your query or mutation

@Query(returns => User)
@UseGuards(GqlAuthGuard)
whoAmI(@CurrentUser() user: User) {
  return this.usersService.findById(user.id);
}

Read More : https://docs.nestjs.com/security/authentication#graphql

Upvotes: 13

Akash Gupta
Akash Gupta

Reputation: 419

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

export const CurrentUser = createParamDecorator(
  (data, context: ExecutionContext) => {
    const ctx = GqlExecutionContext.create(context).getContext();
    return ctx.req.user;
  },
);

Upvotes: 0

M.abdelrhman
M.abdelrhman

Reputation: 1088

Another approach is to validate web token with whatever package you are using, then create decorator get-user.decorator.ts

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';


export const GetUser = createParamDecorator((data, context: ExecutionContext)  => {
 const ctx = GqlExecutionContext.create(context).getContext();
return ctx.user
});

then in your resolver, you can use this decorator (@GetUser() user: User) to access the user

Upvotes: 3

Damon
Damon

Reputation: 4484

Wish I could take any sort of credit here, I'm simply passing along information from the course, NestJS Zero To Hero (absolutely fantastic btw).

For NestJS 7:

// get-user.decorator.ts

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

import { User } from '../../user/entity/user.entity';

export const GetAuthenticatedUser = createParamDecorator((data, ctx: ExecutionContext): User => {
  const req = ctx.switchToHttp().getRequest();
  return req.user;
});

You can implement this however you like. I have an auth.controller that looks something like this:

// auth.controller.ts

import { GetAuthenticatedUser } from './decarator/get-user.decorator';

...

@Controller('api/v1/auth')
export class AuthController {
  constructor(private authService: AuthService) {
    //
  }

  ...

  /**
   * Get the currently authenticated user.
   *
   * @param user
   */
   @Post('/user')
   @UseGuards(AuthGuard())
   async getAuthenticatedUser(@GetAuthenticatedUser() user: User) {
     console.log('user', user);
   }

Result is something like this:

// console.log output:

user User {
  id: 1,
  email: '[email protected]',
  ...
}

Upvotes: 2

grahan
grahan

Reputation: 2398

Finally found the answer ... https://github.com/nestjs/graphql/issues/48#issuecomment-420693225 pointed me into the right direction of creating a user decorator

// user.decorator.ts
import { createParamDecorator } from '@nestjs/common';

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

And then use this in my resolver method:

 import { User as CurrentUser } from './user.decorator';

 @Query(returns => User)
  @UseGuards(GqlAuthGuard)
  whoami(@CurrentUser() user: User) {
    console.log(user);
    return this.userService.findByUsername(user.username);
  }

Now everything works as expected. So all credits of this answer goes to https://github.com/cschroeter

Upvotes: 48

Related Questions