Giter Club home page Giter Club logo

Comments (35)

cschroeter avatar cschroeter commented on March 29, 2024 64

@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);
import { User as CurrentUser } from './user.decorator';

// ...

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

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024 16

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

from graphql.

kctang avatar kctang commented on March 29, 2024 12

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!

from graphql.

w0wka91 avatar w0wka91 commented on March 29, 2024 7

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 

from graphql.

VictorGaiva avatar VictorGaiva commented on March 29, 2024 7

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.

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024 5

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

from graphql.

mateusduraes avatar mateusduraes commented on March 29, 2024 4

@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.

from graphql.

trykers avatar trykers commented on March 29, 2024 3

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 ?

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024 1

@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.

from graphql.

trykers avatar trykers commented on March 29, 2024 1

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

from graphql.

LandazuriPaul avatar LandazuriPaul commented on March 29, 2024 1

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. 😉

from graphql.

callmesoul avatar callmesoul commented on March 29, 2024 1

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

from graphql.

chris-ls avatar chris-ls commented on March 29, 2024 1

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.

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024 1

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

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024

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

from graphql.

w0wka91 avatar w0wka91 commented on March 29, 2024

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 {}

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024

Just pass request as a context value https://www.apollographql.com/docs/apollo-server/v2/essentials/data.html#context

from graphql.

kamilmysliwiec avatar kamilmysliwiec commented on March 29, 2024

It should be pretty easy to implement. Reopening

from graphql.

cschroeter avatar cschroeter commented on March 29, 2024

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

from graphql.

sulthanmamusa avatar sulthanmamusa commented on March 29, 2024

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

Error: Unknown authentication strategy jwt

from graphql.

cschroeter avatar cschroeter commented on March 29, 2024

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

from graphql.

sulthanmamusa avatar sulthanmamusa commented on March 29, 2024

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

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

from graphql.

ghiscoding avatar ghiscoding commented on March 29, 2024

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

from graphql.

orenherman avatar orenherman commented on March 29, 2024

@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.

from graphql.

ghiscoding avatar ghiscoding commented on March 29, 2024

@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.

from graphql.

ghiscoding avatar ghiscoding commented on March 29, 2024

@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

from graphql.

ghiscoding avatar ghiscoding commented on March 29, 2024

@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)
}

from graphql.

MeStrak avatar MeStrak commented on March 29, 2024

@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'),
            };
          },
      ... ]

from graphql.

greg-md avatar greg-md commented on March 29, 2024

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

from graphql.

toondaey avatar toondaey commented on March 29, 2024

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.

from graphql.

mateusduraes avatar mateusduraes commented on March 29, 2024

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

from graphql.

toondaey avatar toondaey commented on March 29, 2024

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?

from graphql.

osamaKu avatar osamaKu commented on March 29, 2024

@mateusduraes, this was very helpful, Thanks

from graphql.

unlight avatar unlight commented on March 29, 2024

Hi, guys. Workaround above #48 (comment) 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?

from graphql.

humayunkabir avatar humayunkabir commented on March 29, 2024

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???

from graphql.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.