NestJS在通过JWT认证的GraphQL解析器中获取当前用户

时间:2019-03-20 20:38:26

标签: node.js jwt graphql nestjs

我目前正在将Passport.js的JWT身份验证实现到NestJS应用程序中。

在某些GraphQL解析器中,我需要访问当前已验证的用户。我知道通行证会将经过身份验证的用户附加到请求对象(至少我希望这是正确的),但是我不知道如何访问解析器中的请求对象。

我关注了问题https://github.com/nestjs/nest/issues/1326和问题中提到的链接https://github.com/ForetagInc/fullstack-boilerplate/tree/master/apps/api/src/app/auth。我在GraphQL解析器方法中看到一些使用@Res() res: Request作为方法参数的代码,但是对于undefined,我总是得到res

这些是我目前的实现方式:

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

需要访问当前用户的解析器

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

智威汤逊策略

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

授权和创建JWT令牌可以正常工作。 GraphQL Guard也适用于不需要访问用户的方法。但是对于需要访问当前经过身份验证的用户的方法,我看不到获得它的方法。

有没有办法完成这样的事情?

4 个答案:

答案 0 :(得分:3)

与往常一样,在完成数小时/天的搜索后,仍未找到解决方案,最后向StackOverflow发布了一个问题,您将在几分钟之内找到答案...

https://github.com/nestjs/graphql/issues/48#issuecomment-420693225为我指明了创建用户装饰器

的正确方向
// 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';

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

现在一切正常。因此,此答案的全部功劳归于https://github.com/cschroeter

答案 1 :(得分:1)

另一种方法是使用您使用的任何程序包验证Web令牌,然后创建装饰器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
});

然后在解析器中,可以使用此装饰器(@GetUser()用户:User) 访问用户

答案 2 :(得分:0)

希望我能在这里获得任何荣誉,我只是传递了NestJS Zero To Hero(绝对很棒的顺便说一句)课程中的信息。

对于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;
});

您可以根据自己的喜好实现此目的。我有一个auth.controller,看起来像这样:

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

结果是这样的:

// console.log output:

user User {
  id: 1,
  email: 'email@test.com',
  ...
}

答案 3 :(得分:0)

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