В настоящее время я внедряю аутентификацию JWT с помощью Passport.js в приложение NestJS.
В некоторых моих преобразователях GraphQL мне нужно получить доступ к текущему аутентифицированному пользователю. Я знаю, что паспорт прикрепит аутентифицированного пользователя к объекту запроса (по крайней мере, я надеюсь, что это правильно), но я не знаю, как получить доступ к объекту запроса внутри резолвера.
Я следил за выпуском https://github.com/nestjs/nest/issues/1326 и упомянутой ссылкой https://github.com/ForetagInc/fullstack-boilerplate/tree/master/apps/api/src/app/auth внутри выпуска. Я видел некоторый код, который использует @Res() res: Request в качестве параметра метода в методах распознавателя GraphQL, но я всегда получаю 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.info(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.info(req);
return this.userService.findByUsername('aw');
}
}
Стратегия JWT
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 также отлично работает для методов, которым не требуется доступ к пользователю. Но для методов, которым нужен доступ к текущему аутентифицированному пользователю, я не вижу способа его получить.
Есть ли способ сделать что-то подобное?
Не могли бы вы поделиться ссылкой на ваш репозиторий GitHub? Я новичок в Nest.js, я также использую GraphQL, и я застрял в реализации аутентификации. Спасибо!





Наконец-то нашел ответ... https://github.com/nestjs/graphql/issues/48#issuecomment-420693225 указал мне правильное направление создания пользовательский декоратор
// user.decorator.ts
import { createParamDecorator } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data, req) => req.user,
);
А затем используйте это в моем методе распознавателя:
import { User as CurrentUser } from './user.decorator';
@Query(returns => User)
@UseGuards(GqlAuthGuard)
whoami(@CurrentUser() user: User) {
console.info(user);
return this.userService.findByUsername(user.username);
}
Теперь все работает как положено. Таким образом, все кредиты этого ответа принадлежат https://github.com/cschroeter.
Это действительно должно быть частью фреймворка, и в документах по умолчанию что-то подобное отсутствует, что заставляет меня задуматься, действительно ли люди его используют, лол.
Спасибо! Кроме того, поскольку это рабочий ответ, вы должны принять его, даже если он ваш собственный. Еще раз спасибо, я искал решение как минимум час, прежде чем нашел это, и оно сработало отлично.
В v7 Nest createParamDecorator изменился. Получение пользователя выполняется через контекст GraphQL. См. здесь: docs.nestjs.com/graphql/other-features#custom-decorators
Другой подход — проверить веб-токен с помощью любого пакета, который вы используете, а затем создать декоратор 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: User) для доступа к пользователю
Хотел бы я получить здесь какую-либо оценку, я просто передаю информацию из курса 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.info('user', user);
}
Результат примерно такой:
// console.info output:
user User {
id: 1,
email: '[email protected]',
...
}
Обратите внимание, что это будет работать только для служб REST. При использовании GraphQL вам нужно будет использовать контекст, связанный с GraphQL GqlExecutionContext.create(context).getContext(), а не ctx.switchToHttp().getRequest()
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;
},
);
Вместо того, чтобы реализовывать свой собственный метод
canActivateв своемGqlAuthGuard, вы должны создать методgetRequestи вернутьGqlExecutionContext.create(context).getContext().req;. На мой взгляд, это лучший подход.