Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nest.JS use AuthGuard as Middleware for GraphQL

I'm trying to make secure my GraphQL endpoint with passportJS in order that every call to this endpoint uses the AuthGuard for validating the token and setting the user on request.user, just as it does in a controller with this code:

@Get('findAll')
@UseGuards(AuthGuard('jwt'))
findAll(@Req() request): Promise<Array<Thing>> {
    return this.thingService.findByUser(request.user.email);
}

The thing is I want to use it in the graphQL endpoint, which is created like this:

consumer
    .apply(graphiqlExpress({ endpointURL: '/graphql' }))
    .forRoutes('/graphiql')
    .apply(
        graphqlExpress(req => ({ schema, rootValue: req })),
        ¿?,
    )
    .forRoutes('/graphql');

I suppose I can just set it like a middleware function after the graphqlExpress function, but I have not been successful. Any thoughts?

Thank you in advance!

Edit

As a workaround I have implemented the solution proposed on Nest Docs where it uses the @UseGuard in every query/mutation that must be protected.

However, I want to protect the entire endpoint so that the guard is not called for every protected resolver, but only once on the main request. Is this even possible?

like image 530
Robert Mengual Avatar asked Aug 21 '18 08:08

Robert Mengual


People also ask

What is AuthGuard in NestJS?

Authorization guard The AuthGuard that we'll build now assumes an authenticated user (and that, therefore, a token is attached to the request headers). It will extract and validate the token, and use the extracted information to determine whether the request can proceed or not.

What is middleware in NestJS?

Middleware is a function which is called before the route handler. Middleware functions have access to the request and response objects, and the next() middleware function in the application's request-response cycle. The next middleware function is commonly denoted by a variable named next .

What is JWT in NestJS?

In this post, we will learn how to implement NestJS JWT Authentication using Passport JWT Strategy. JWT stands for JSON Web Tokens. Basically, these tokens are issued by the server after user authentication and can be used for further requests as long as the token is valid.


1 Answers

This technically is possible, but it's a pretty sloppy thing to write, and there's absolutely no guarantees it will work with Fastify so heads up. The meat of the functionality comes from the module where you implement the middleware. I ended up doing this all with the AppModule which I do not suggest (at least not all of the code there), but it works nonetheless.

You need to make the guard a custom provider so it can be injected into any context.

Then you need to mock up the ExecutionContext using req, res, next. This is easier said than done if you want type safety, but if you don't care about that (which I didn't for this) then slap up an as any and call it a day.

After that, in the middleware consumer you run the apply and make use of this.guard.canActivate with that mock ExecutionContext you created. Make this middleware async and await the canActivate call. Check that it comes back as true and if not then throw new <ErrorOfYourChoice>() and boom. It's set up. The code would look (vaguely) like this:

import {
  BadRequestException,
  CanActivate,
  Inject,
  MiddlewareConsumer,
  Module,
  NestModule,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AppResolver } from './app.resolver';
import { GraphQLModule } from '@nestjs/graphql';
import { JwtModule } from '@nestjs/jwt';
import { AuthGuard, PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
    }),
    JwtModule.register({ secret: 'secret' }),
    PassportModule.register({ defaultStrategy: 'jwt' }),
  ],
  controllers: [AppController],
  providers: [
    AppService,
    AppResolver,
    JwtStrategy,
    { provide: 'CustomGuard', useClass: AuthGuard() },
  ],
})
export class AppModule implements NestModule {
  constructor(@Inject('CustomGuard') private readonly guard: CanActivate) {}

  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(async (req, res, next) => {
        const canActivate = await this.guard.canActivate({
          switchToHttp: () => ({
            getRequest: () => req,
            getResponse: () => res,
            getNext: () => next,
          }),
        } as any);
        if (canActivate) {
          next();
        } else {
          throw new BadRequestException();
        }
      })
      .forRoutes('graphql');
  }
}

You can check this repository for everything wired up and working. Login with POST /login -d 'username=test1&password=changeme', get the JWT and play around with it as you like.

like image 163
Jay McDoniel Avatar answered Dec 31 '22 09:12

Jay McDoniel