Как настроить JWT аутентификацию с Passport в NestJS?

MiddleNestJS · Backend·Обновлено 29 июля 2026
Коротко
Для JWT-аутентификации в NestJS используют пакеты @nestjs/passport, passport-jwt и @nestjs/jwt: создают JwtStrategy с валидацией токена, JwtAuthGuard для защиты маршрутов и AuthModule с JwtModule.register для конфигурации.

Настройка JWT аутентификации с Passport в NestJS

Установка зависимостей

Для работы с JWT и Passport необходимы следующие пакеты:

npm install @nestjs/passport @nestjs/jwt passport passport-jwt
npm install -D @types/passport-jwt

Создание JwtStrategy

JwtStrategy отвечает за валидацию токена при каждом запросе. Passport автоматически извлекает токен из заголовка Authorization: Bearer <token> и передаёт декодированный payload в метод validate.

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      // Извлекаем токен из заголовка Authorization: Bearer
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      // Запрещаем просроченные токены
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    });
  }

  // Payload уже проверен библиотекой — здесь только бизнес-логика
  async validate(payload: { sub: number; email: string }) {
    return { userId: payload.sub, email: payload.email };
  }
}

Создание AuthGuard

Стандартный подход — расширить AuthGuard('jwt') для повторного использования:

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

AuthModule: регистрация JwtModule и Passport

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './strategies/jwt.strategy';

@Module({
  imports: [
    PassportModule,
    // Асинхронная конфигурация для получения секрета из env
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        secret: config.get<string>('JWT_SECRET'),
        signOptions: { expiresIn: '1h' },
      }),
    }),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

AuthService: выдача токена

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService,
  ) {}

  async validateUser(email: string, password: string) {
    const user = await this.usersService.findByEmail(email);
    if (user && await bcrypt.compare(password, user.passwordHash)) {
      const { passwordHash, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: { id: number; email: string }) {
    // sub — стандартное поле JWT для идентификатора субъекта
    const payload = { sub: user.id, email: user.email };
    return { access_token: this.jwtService.sign(payload) };
  }
}

Защита маршрутов

import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('profile')
export class ProfileController {
  // Только аутентифицированные пользователи могут получить профиль
  @UseGuards(JwtAuthGuard)
  @Get()
  getProfile(@Request() req) {
    // req.user заполняется результатом JwtStrategy.validate()
    return req.user;
  }
}

Глобальная защита через APP_GUARD

Если большинство маршрутов требуют аутентификации, можно применить guard глобально и использовать @Public() для исключений:

// Декоратор для публичных маршрутов
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

Что хочет услышать интервьюер

Знание роли каждого пакета: @nestjs/passport как обёртка, passport-jwt как реализация стратегии, @nestjs/jwt для подписи и верификации токенов

Понимание потока аутентификации: запрос → Guard → Strategy.validate() → req.user → контроллер

Объяснение, зачем payload в JWT содержит поле sub, и почему ignoreExpiration должен быть false

Умение хранить JWT_SECRET в переменных окружения через ConfigService, а не хардкодить

Знание разницы между глобальным APP_GUARD и декоратором @UseGuards на методе/контроллере

Пример: JwtStrategy — валидация токена

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    });
  }

  async validate(payload: { sub: number; email: string }) {
    // Возвращаемое значение станет req.user
    return { userId: payload.sub, email: payload.email };
  }
}

Пример: AuthModule — регистрация зависимостей

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JwtStrategy } from './strategies/jwt.strategy';
import { AuthService } from './auth.service';

@Module({
  imports: [
    PassportModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        secret: config.get<string>('JWT_SECRET'),
        signOptions: { expiresIn: '1h' },
      }),
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

Пример: Защита маршрута через JwtAuthGuard

import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';

@Controller('profile')
export class ProfileController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProfile(@Request() req) {
    // req.user содержит { userId, email } из JwtStrategy.validate()
    return req.user;
  }
}

Типичные ошибки

Хранение JWT_SECRET напрямую в коде вместо использования ConfigService и .env файла

Возврат лишних данных (например, passwordHash) из метода validate() в JwtStrategy — они попадают в req.user

Установка ignoreExpiration: true в продакшне, что делает токены вечными даже после истечения срока

Забывают экспортировать JwtModule или AuthService из AuthModule, из-за чего другие модули не могут их использовать

Путаница между LocalStrategy (логин по паролю) и JwtStrategy (защита маршрутов) — используют JwtStrategy для логина

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее