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 ?
canActivate
method in yourGqlAuthGuard
you should create agetRequest
method and returnGqlExecutionContext.create(context).getContext().req;
. This is a better approach in my opinion. – Astrology