Вопросы по Nest.js

Как реализовать механизм обновления токенов в NestJS?

Механизм обновления токенов (token refresh) является важной частью систем аутентификации, так как позволяет поддерживать пользовательские сессии без необходимости повторной аутентификации. В данной статье мы рассмотрим, как реализовать автоматическую стратегию обновления токенов в приложении на NestJS.

Основные концепции

  1. Токены доступа и токены обновления:
    • Токен доступа (access token): используется для доступа к защищенным ресурсам и имеет короткое время жизни (например, 15 минут).
    • Токен обновления (refresh token): используется для получения нового токена доступа и имеет более длительное время жизни (например, 7 дней).
  2. Стратегия обновления токенов:
    • Когда токен доступа истекает, клиент отправляет токен обновления на сервер, чтобы получить новый токен доступа.
    • Сервер проверяет токен обновления, и если он действителен, возвращает новый токен доступа.

Реализация в NestJS

Установка необходимых зависимостей

Сначала установим необходимые пакеты:

npm install @nestjs/jwt passport-jwt

Создание модуля аутентификации

Создадим модуль аутентификации, который будет содержать логику работы с токенами.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';

@Module({
  imports: [
    JwtModule.register({
      secret: 'your-secure-secret', // Убедитесь, что секрет безопасен
      signOptions: { expiresIn: '15m' }, // Время жизни токена доступа
    }),
  ],
  providers: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}

AuthService: Логика аутентификации и обновления токена

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async generateAccessToken(userId: number) {
    return this.jwtService.sign({ userId });
  }

  async generateRefreshToken(userId: number) {
    return this.jwtService.sign({ userId }, { expiresIn: '7d' }); // Токен обновления
  }

  async refreshTokens(userId: number, refreshToken: string) {
    const payload = this.jwtService.verify(refreshToken); // Проверяем токен обновления
    if (!payload) throw new Error('Invalid refresh token');

    // Генерируем новый токен доступа
    const accessToken = await this.generateAccessToken(userId);
    return { accessToken };
  }
}

AuthController: Эндпоинт для обновления токена

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('refresh')
  async refreshTokens(@Body('refreshToken') refreshToken: string) {
    // Здесь userId должен быть получен из базы данных, часто в реальных приложениях
    const userId = /* logic to get userId from refresh token */;
    return this.authService.refreshTokens(userId, refreshToken);
  }
}

Клиентская сторона

Для обновления токена с клиентской стороны (например, с использованием fetch):

async function refreshToken() {
  const response = await fetch('/auth/refresh', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ refreshToken: localStorage.getItem('refreshToken') }),
  });

  if (response.ok) {
    const data = await response.json();
    localStorage.setItem('accessToken', data.accessToken);
  } else {
    // Обработка ошибки: токен обновления недействителен
    console.error('Unable to refresh token');
  }
}

Заключение

Реализуя механизм обновления токенов в NestJS, вы улучшаете пользовательский опыт, позволяя пользователям оставаться в системе без частой повторной аутентификации. Этот пример служит базовым ориентиром, который можно адаптировать под более сложные сценарии в зависимости от требований вашего приложения.