Graphql: Support AuthGuard

Created on 3 Sep 2018  Â·  35Comments  Â·  Source: nestjs/graphql

I'm submitting a...


[X] Regression 
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior


I can't access the request within my guards anymore. The request within the execution context is always undefined. It looks like the request doesn't get passed through apollo server correctly.

Expected behavior


The request should be accessible within guards.

Minimal reproduction of the problem with instructions


https://github.com/w0wka91/nest/tree/graphql-passport-integration

What is the motivation / use case for changing the behavior?


The request should be accessible to authenticate the user. Furthermore this behavior doesn't let me integrate nestjs/passport into my application.

Environment


Nest version: 5.3.0

 This error accurs since apollo server was updated
For Tooling issues:
- Node version: 10.9  
- Platform:  Mac 

Others:

enhancement

Most helpful comment

@w0wka91 Here is a workaround you can use:

First pass the original req object into the graphql context.

GraphQLModule.forRoot({
      typePaths: ['./**/*.graphql'],
      context: ({ req }) => ({ req })
})

Create a fake execution context

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

@Injectable()
export class GraphqlAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    const ctx = GqlExecutionContext.create(context);
    const { req } = ctx.getContext();
    return super.canActivate(new ExecutionContextHost([req]));
  }
}
// user.decorator.ts
import { createParamDecorator } from '@nestjs/common';

export const User = createParamDecorator((data, [root, args, ctx, info]) => ctx.req.user);

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

// ...

@UseGuards(GraphqlAuthGuard)
@Query('doSomething')
async doSomething(@CurrentUser() user: User): Promise {
return await this.userService.findById(user.id);
}

All 35 comments

ExecutionContext is a wrapper around the arguments array. You cannot access request in GraphQL app unless, for instance, you pass it as a context value. That is explained here https://docs.nestjs.com/graphql/tooling and in more details here https://docs.nestjs.com/guards

It's even undefined when i pass it as a context value. Do you have an example how I'm supposed to pass the request as a context value?
I tried to do it like this but the request is still undefined:


@Module({
imports: [
CatsModule,
GraphQLModule.forRoot({
typePaths: ['./*/.graphql'],
context: ({req}) => ({...req}),
}),
],
})
export class ApplicationModule {}

Ah, yes thanks. Now I'm able to access the request through the context value in my own guards. But i still cant get it work together with the passport module. It worked all before i upgraded the graphql module. Do i have to write my own guard or is there any way that i can use the provided AuthGuard?
At the moment i get this error:


TypeError: Cannot read property 'headers' of undefined" at JwtStrategy._jwtFromRequest

It should be pretty easy to implement. Reopening

@kamilmysliwiec I can not wrap my head around it. Could you tell how to switch to the original express request? This would be awesome

@w0wka91 Here is a workaround you can use:

First pass the original req object into the graphql context.

GraphQLModule.forRoot({
      typePaths: ['./**/*.graphql'],
      context: ({ req }) => ({ req })
})

Create a fake execution context

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

@Injectable()
export class GraphqlAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    const ctx = GqlExecutionContext.create(context);
    const { req } = ctx.getContext();
    return super.canActivate(new ExecutionContextHost([req]));
  }
}
// user.decorator.ts
import { createParamDecorator } from '@nestjs/common';

export const User = createParamDecorator((data, [root, args, ctx, info]) => ctx.req.user);

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

// ...

@UseGuards(GraphqlAuthGuard)
@Query('doSomething')
async doSomething(@CurrentUser() user: User): Promise {
return await this.userService.findById(user.id);
}

Just pushed an example: https://docs.nestjs.com/techniques/authentication (GraphQL)

Hi cschroeter, I tried with your code, but not working and I got the following error message:

Error: Unknown authentication strategy jwt

@sulthanmamusa please have a look here https://docs.nestjs.com/techniques/authentication

@sulthanmamusa please have a look here https://docs.nestjs.com/techniques/authentication

Thank you very much. Now I got it...

Hi guys, I have been trying to get AuthGuard to work in my GraphQL server with @nestjs/passport and @nestjs/jwt modules the whole morning....but still can't get it to work.

The GraphqlAuthGuard I extended AuthGuard is called but somehow my class JwtStrategy extends PassportStrategy(Strategy) does not work properly. The GraphqlAuthGuard is called and request is passed correctly but...

...I got Error: [object Object] at GraphqlAuthGuard.handleRequest in the GQL response with a 401 Unauthorized.

Any place I can see a full example that includes code for both PassportStrategy and AuthGuard for GraphQL? Appreciate some help here. Thanks!

Same problem, everything is ok with REST, when i try to use GqlAuthGuard, the validate() function in jwt.strategy.ts was never called... Any idea ?

@nestjs/passport is not compatible with @nestjs/graphql (unless you extend the class and pass correct arguments to the parent method) because passport library needs access to 3 arguments ([req, res, next]) that aren't present in a typical GraphQL app.

I read the doc many and many times, my problem was that I wrote "Baerer" instead of "Bearer"... All works fine now...

Just a simple tip for newcomers running their app with Nest v6.

As I wanted to make use of the integration of TypeGraphQL in Nest v6 — btw, thank you for the great job with this new release @kamilmysliwiec! — I've migrated my app.

So I had a GraphqlAuthGuard using the workaround suggested by @cschroeter and it was fine until my migration to Nest v6 which changes one file name in @nestjs/core package.

For people wishing to integrate the above workaround with Nest v6, change the import of the ExecutionContextHost to:

import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

The only difference is the dash instead of the point in execution-context-host. 😉

I All works fine now。 my problem is frontend headers write Authorization to authorization

Hey guys, I followed this Medium post to add OAuth to my application and it has it's own AuthModule and another module CatsModule for the GraphQL implementation. The problem that I'm having is that from the GraphQL Sample I cannot get the User in the Context Request, it's always null or undefine. I think it might be because Context Requests are isolated between NestJS modules? I'm totally new so I'm not sure... I just started this week to learn NestJS, I'm on latest version 6.6.7.

In the auth.controller, I can get the User in the Context Request like this

  @Get('protected')
  @UseGuards(AuthGuard('jwt'))
  protectedResource(@Request() req) {
    return { result: 'JWT is working!', user: req.user }; // <- all good
  }

but this User is not available in any file of my other module (cats.resolver, gqp-auth.guard), I trace the problem up to the GraphQLModule which is imported in the AppModule, the Context Request in there doesn't have the User and so none of the other file will get it since they all derived from the Context passed in the GraphQLModule.

@Module({
  imports: [
    AuthModule,
    CatsModule,
    GraphQLModule.forRoot({
      autoSchemaFile: 'schema.gql',
      context: ({ req }) => {
        console.log('user', req.user); // <-- this is always undefined
        return ({ req });
      },
    }),
    MongooseModule.forRoot('mongodb://localhost/nest', { useNewUrlParser: true }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

The GraphQL Guard still seems to work, I guess it's because it only checks that a context exist, but I just can't get the connected user. I would be really happy to have some help/guidance in fixing this.

Note that I'm totally new to NestJS and I wanted to learn it since for a long time (personal project) and this is basically my first week at it. Also I'm more of a frontend dev and my NodeJS knowledge is a bit low, though I played a bit with Koa some time ago.

EDIT

I actually got it working, but I'm not totally sure how it all works. I do have undefined in all the areas that I wrote earlier, but in the end, I do get the user in the createParamDecorator. Anyhow, this now works

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

@ghiscoding i encountered the same scenario.
I can get the user from the http context but not from the GraphQL context.
I wonder how you overcame it ? I mean the request doesn't pass the guard to get to the paramDecorater..

This is quite frustrating.. it seems that the graphql context is created without first invoking the desserialzeUser and thus no user is present on the request.

@orenherman

I actually got it working, but I'm not totally sure how it all works. I do have undefined in all the areas that I wrote earlier, but in the end, I do get the user in the createParamDecorator. Anyhow, this now works

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

So I just removed the console.log and it was working with the code I wrote in my last post.

So far, this discussion has addressed only the Jwt Strategy but I am unsure how to get the Local Strategy to work.
To summarize the steps involved with the Jwt Strategy:

  • Put the Request into the GraphQL context for every request
GraphQLModule.forRoot({
      autoSchemaFile: 'schema.gql',
      context: ({ req }) => ({ req })
})
  • Provide an implementation for getRequest inside of your Guard which will grab the Request from the GraphQL context
getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);

    return ctx.getContext().req;
}

From this point onward, the typical JwtStrategy can proceed as it only needs to access the Authorization header found in the request object.

This also leads in to being able to define a GraphQL specific @User decorator.

export const GqlUser = createParamDecorator(
  (data, [root, args, ctx, info]) => ctx.req.user,
);

This works because our Request object is now available at ctx.req and this req is passed to Passport thanks to the getRequest() method we implemented for the Guard, which then attaches a user object upon successful authentication.

I have been trying to get an AuthGuard using the Local Strategy working, but not sure how to proceed.
As I understand, the issue is that the Local Strategy is expecting a body on the request that looks something like

{
  "username": "bob",
  "password": "password123"
}

but is instead getting something like

{
  "query": "mutation { signIn(data: {username: "bob", password: "password123"}) { accessToken } }"
}

It's not a huge problem in this specific case, since AuthGuard('local') is typically only used in very few places, such as a sign-in mutation and so the logic can just be explicitly handled in the resolver.

As for local strategy with GraphQL, you shouldn't use passport. I would recommend creating your own guard instead

@kamilmysliwiec but how do we get access to the user from a guard (sorry I'm new to NestJS)?
In my case I use PassPort and GraphQL and if I do my own custom guard, I don't have access to the useer and I don't know how to get it. It should be part of the request, so why I can't get it?

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

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

I can get the user from the @CurrentUser() but I can't get it inside the guard. I tried so many things, I don't know what to do anymore.

I also posted a Stack Overflow question

@chris-ls
I got an answer to my SO question, I finally got the canActivate to work like I wanted with roles check... hopefully this would help you as well.

Cheers

async canActivate(context: ExecutionContext): Promise<boolean> {
    await super.canActivate(context);
    const ctx = GqlExecutionContext.create(context);
    const req = ctx.getContext().req;
   console.log('graphql guard user', req && req.user)
}

I was able to authenticate HTTP, GraphqL Queries and GraphqL Subscriptions using JWT with the following solution, without using @nestjs/passport.

Fist, add context: ({ req }) => ({ req }) to the GraphQLModule import in your root module imports:

@Module({
  imports: [
    GraphQLModule.forRoot({
      typePaths: ['./**/*.gql'],
      installSubscriptionHandlers: true,
      context: ({ req }) => ({ req }), // <------ HERE
    }),
    UsersModule,
  ],
  providers: [
    MainService,
  ],
  controllers: [MainController],
})

Create auth.module.ts with the following content:

import { JwtModule } from '@nestjs/jwt';
import { Module } from '@nestjs/common';

import { HttpAuthGuard, WsAuthGuard, GqlAuthGuard } from './auth.guard';
import { AuthService } from './auth.service';

@Module({
  imports: [
    JwtModule.register({
      secret: process.env.PRIVATE_KEY,
      signOptions: { expiresIn: process.env.TOKEN_EXPIRATION },
    }),
  ],
  providers: [
    HttpAuthGuard,
    WsAuthGuard,
    GqlAuthGuard,
    AuthService
  ],
  exports: [HttpAuthGuard, WsAuthGuard, GqlAuthGuard, AuthService],
})
export class AuthModule { }

Create auth.service.ts with the following content:

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

import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private readonly jwtService: JwtService,
  ) { }

  GenerateToken(payload: any) {
    return this.jwtService.sign(payload);
  }

  ValidateToken(token: string) {
    try {
      this.jwtService.verify(token);
      return true;
    } catch (error) {
      return error.name;
    }
  }
}

Finally, create auth.guard.ts with the following content:

import {
  Injectable,
  ExecutionContext,
  CanActivate,
  BadRequestException,
  UnauthorizedException
} from "@nestjs/common";

import { AuthService } from "./auth.service";

@Injectable()
export class HttpAuthGuard implements CanActivate {
  constructor(
    private readonly auth: AuthService,
  ) { }

  canActivate(context: ExecutionContext) {
    // Get the header
    const authHeader = context.switchToHttp().getRequest().headers.authorization as string;

    if (!authHeader) {
      throw new BadRequestException('Authorization header not found.');
    }
    const [type, token] = authHeader.split(' ');
    if (type !== 'Bearer') {
      throw new BadRequestException(`Authentication type \'Bearer\' required. Found \'${type}\'`);
    }
    const validationResult = this.auth.ValidateToken(token); 
    if (validationResult === true) {
      return true;
    }
    throw new UnauthorizedException(validationResult);
  }
}

@Injectable()
export class GqlAuthGuard implements CanActivate {
  constructor(
    private readonly auth: AuthService,
  ) { }

  canActivate(context: ExecutionContext) {
    // Get the header
    const authHeader = context.getArgs()[2].req.headers.authorization as string;

    if (!authHeader) {
      throw new BadRequestException('Authorization header not found.');
    }
    const [type, token] = authHeader.split(' ');
    if (type !== 'Bearer') {
      throw new BadRequestException(`Authentication type \'Bearer\' required. Found \'${type}\'`);
    }
    const validationResult = this.auth.ValidateToken(token);
    if (validationResult === true) {
      return true;
    }
    throw new UnauthorizedException(validationResult);
  }
}

@Injectable()
export class WsAuthGuard implements CanActivate {
  constructor(
    private readonly auth: AuthService,
  ) { }

  canActivate(context: ExecutionContext) {
    // Since a GraphQl subscription uses Websockets,
    //     we can't pass any headers. So we pass the token inside the query itself
    const token = context.switchToWs().getData().token;

    if (!token) {
      throw new BadRequestException('Authentication token not found.');
    }

    const validationResult = this.auth.ValidateToken(token);
    if (validationResult === true) {
      return true;
    }
    throw new UnauthorizedException(validationResult);
  }
}

That's it. We can now use the guards by adding AuthModule to module imports you want to use.

Usage examples:

  • HTTP:
@Get('profile')
@UseGuards(HttpAuthGuard)
getProfile() {
  return 'Got that';
}
  • GraphQL Query:
@Mutation()
@UseGuards(GqlAuthGuard)
async setOnline(@Args('userId') userId: string) {
  return await this.service.SetOnline(userId);
}
  • GraphQL Subscription:

Now for GraphQL Subscriptions, we have to pass the token inside the query itself. I defined the userOnline Subscription like this:

type Subscription {
  userOnline(token:String): UserSubscribeDto!
}

then the query should be something like:

subscription{
  userOnline(token:"<YOUR TOKEN>"){
    userId
  }
}

With the things above defined, we can use the guard like this:

@Subscription()
@UseGuards(WsAuthGuard)
userOnline() {
  return this.publisher.asyncIterator('userOnline');
}

The idea of this implementation is to keep it, simple, transparent and easy to understand. For the initial use case, protecting an endpoint from unauthenticated access, it should be enough. There are still things that could be added, like roles and a way to access the token payload inside the handlers. I'll try to implement these and might comeback with an update.

But for now this should be better the the _magical_ ~and confusing~ implementation on the Oficial documentation. I really recommend you not to use @nestjs/passport, as it ends up adding unnecessary abstraction to the setup. I'll go even further and say that the Documentation should remove its usage completely. It adds a whole lot of overhead to something that should've been simple and ends up confusing beginners. If someone needs a solution like @nestjs/passport, they will look for it.

PS: I decided to post this here as it was one of the threads I found when trying to find a solution for this. This comment is targeted and people who might be going through the same problem I was and found this.

@VictorGaiva thank you!! I've been struggling to figure out how to get it working for subscriptions and yours is the only clear explanation I've found.

I am using Auth0 with Passport, I already had a GraphQL auth guard implemented which worked for normal requests, but not for subscriptions. I'm also using TypeGraphQL and my schema is auto generated so my solution is inspired by yours but slightly different.

Read token from the arguments for subscription in my resolver:

  @Subscription(returns => ItemType)
  @UseGuards(new GqlAuthGuard('jwt'))
  itemCreatedOrUpdated(@Args('token') token: string) {
    return pubSub.asyncIterator('itemCreatedOrUpdated');
  }

Instead of a separate auth guard for websockets, I build the context using the passed token parameter if it is missing (if req is undefined):

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

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

    //Subscription requests don't have context, so check the token for header
    if (typeof req === 'undefined') {
      const token = context.switchToWs().getData().token;

      if (!token) {
        throw new BadRequestException('Authentication token not found.');
      }

      //build request context so that it can be read by super.canActivate
      const authHeader = {
        authorization: token,
      };
      req = { headers: authHeader };
    }

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

Finally in the front end (I'm using vue-apollo), pass the token as an argument which is currently in localStorage in my code:

subscribeToMore: [
        {
          document: gql`
            subscription subItems($token: String!) {
              itemCreatedOrUpdated(token: $token) {
                id
                title
                estimate
                status
                description
              }
            }
          `,
          // Variables passed to the subscription. Since we're using a function,
          // they are reactive
          variables() {
            return {
              token: 'Bearer ' + localStorage.getItem('gqlbear'),
            };
          },
      ... ]

After hours of searching how to enable gql auth guard for subscriptions, I came up with a working solution based on https://www.apollographql.com/docs/graphql-subscriptions/authentication/ without much refactoring.

On client side, send headers via connectionParams payload:

connectionParams: async () => {
  const auth = await authStorage.get().toPromise();

  if (auth) {
    return {
      headers: {
        Authorization: `Bearer ${auth.access_token}`
      },
    }
  }
}

On server side, listen for the payload and send it as a request via context config:

context: ({ req, connection }) => ({ req: req || connection?.context }),
subscriptions: {
  // Send client connect payload to the connection context
  onConnect: connectionParams => connectionParams,
}

And voila, you can use the base gql auth guard on your subscriptions:

@Subscription(returns => Boolean)
@UseGuards(GqlAuthGuard)
mySubscription() {
  return this.pubSub.asyncIterator('mySubscription');
}

Please, note that the API for the createParamDecorator has changed in v7.0.0. The example (from the docs) is updated now: https://docs.nestjs.com/techniques/authentication#graphql

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;
  },
);

Please, note that the API for the createParamDecorator has changed in v7.0.0. The example (from the docs) is updated now: https://docs.nestjs.com/techniques/authentication#graphql

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;
  },
);

Hi @kamilmysliwiec, so I'm relatively new to nestjs, but not graphql. However I've tried this method but keep getting this error: context.getType is not a function.

My guard below:

import { AuthService } from '../../auth.service';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Injectable, ExecutionContext } from '@nestjs/common';

@Injectable()
export class LoginGuard extends AuthGuard('local') {
    // ...
    getRequest(context: ExecutionContext) {
        const ctx = GqlExecutionContext.create(context);

        const { request } = ctx.getContext();

        request.body = ctx.getArgs();

        return request;
    }
}

and custom decorator is just as the same above.
Great job on the framework btw.

UPDATE:
Solved it by upgrading my nestjs packages.

Does someone has a working example with the authentication process for GraphQL with NestJS using JWT with @nest/passport.

I could not make it work with what we have in the docs, and, in my case, it would be good to use @nest/passport because I am going to have also Facebook and Google login. Thanks

Does someone has a working example with the authentication process for GraphQL with NestJS using JWT with @nest/passport.

I could not make it work with what we have in the docs, and, in my case, it would be good to use @nest/passport because I am going to have also Facebook and Google login. Thanks

Can you share what you've implemented so far, please?

@toondaey I wasn't able to use the integration with @nest/passport for GraphQL. I believe the best option is to not use @nestjs/passport with GraphQL.

I end up using the following approach (that works pretty well)

// auth.module.ts
@Module({
  imports: [
    UserModule,
    JwtModule.register({
      secret: 'my-app-secret',
    }),
  ],
  providers: [AuthResolver, AuthService, GqlAuthGuard],
  exports: [GqlAuthGuard],
})
export class AuthModule {}
// gql-auth.guard.ts
@Injectable()
export class GqlAuthGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {}

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

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = this.getRequest(context);
    const authHeader = req.headers.authorization as string;

    if (!authHeader) {
      throw new BadRequestException('Authorization header not found.');
    }
    const [type, token] = authHeader.split(' ');
    if (type !== 'Bearer') {
      throw new BadRequestException(`Authentication type \'Bearer\' required. Found \'${type}\'`);
    }
    const { isValid, user } = await this.authService.validateToken(token);

    if (isValid) {
      req.user = user;
      return true;
    }
    throw new UnauthorizedException('Token not valid');
  }
}
// auth.service.ts
@Injectable()
export class AuthService {
  constructor(private jwtService: JwtService, @InjectRepository(User) private usersRepository: Repository<User>) {}

  async validateUser(email: string, password: string): Promise<LoginResponse> {
    const user = await this.usersRepository.findOne({ email });
    if (!user) {
      throw 'user not found';
    }
    if (user.password === password) {
      const token = this.jwtService.sign({
        userId: user.id,
        email: user.email,
      });
      return { user, token };
    }
  }

  async validateToken(token: string): Promise<{ isValid: boolean; user?: User }> {
    try {
      const { userId } = this.jwtService.verify(token);
      const user = await this.usersRepository.findOne(userId);
      return { user, isValid: true };
    } catch (e) {
      return { isValid: false };
    }
  }
}

// current-user.decorator.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;
});
@Resolver(() => User)
export class AuthResolver {
  constructor(private authService: AuthService) {}

  @Mutation(() => LoginResponse, { name: 'login' })
  async login(@Args('loginData') { email, password }: LoginInput): Promise<LoginResponse> {
    return this.authService.validateUser(email, password);
  }

  @UseGuards(GqlAuthGuard)
  @Query(() => User, { name: 'whoAmI' })
  async whoAmI(@CurrentUser() user: User): Promise<User> {
    return user;
  }
}

In order to finish and make it a real-life project (let's say), I just need to implement the process to encrypt the password.

I hope it may help someone that has the same problem.
I end up using a little bit of each one of the previous answers.

@mateusduraes, this was very helpful, Thanks

Hi, guys. Workaround above https://github.com/nestjs/graphql/issues/48#issuecomment-420693225 for @CurrentUser decorator works, but it requires applying GraphqlAuthGuard guard, what if I want CurrentUser value be optional in my resolver?

    // no guard here
    @Query(() => User)
    async user(
        @CurrentUser() currentUser?: PassportUserFields,
    ) {
       // currentUser is always undefined, context.req.user never assigned by passport
       // even if Authorization header was sent
        return currentUser;
    }

So, is there way to apply logic for assigning req.user from nest/passport?

Same problem, everything is ok with REST, when I try to use GqlAuthGuard, the validate() function in jwt.strategy.ts was never called... Any idea?

Any update on this problem???

Was this page helpful?
0 / 5 - 0 ratings