PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    JWT аутентификация в Node.js: access и refresh токены

    JWT аутентификация в Node.js: access и refresh токены

    Аватар автора JWT аутентификация в Node.js: access и refresh токены

    Антон Ларичев

    Иконка календаря27 мая 2026
    Node.jsJWTАутентификацияБезопасностьBackendmiddleИконка уровня middle
    Картинка поста JWT аутентификация в Node.js: access и refresh токены

    Введение

    JWT (JSON Web Token) — это компактный стандарт передачи данных между сторонами в виде подписанного JSON-объекта. В Node.js JWT часто используется для реализации аутентификации в REST API и микросервисах: сервер не хранит сессии, а доверяет токену, проверяя его подпись.

    Классическая схема включает два типа токенов: access — короткоживущий, используется для доступа к защищённым ресурсам, и refresh — долгоживущий, нужен для обновления access без повторного ввода пароля. Такой подход снижает риски: если access утечёт, окно атаки ограничено его временем жизни.

    В статье разберём генерацию, проверку и ротацию токенов, а также типичные ошибки реализации.

    Структура JWT

    Токен состоит из трёх частей, разделённых точкой: header.payload.signature. Header описывает алгоритм подписи, payload содержит claims (sub, iat, exp и кастомные поля), signature — криптографическая подпись HMAC или RSA.

    Важно понимать: payload не шифруется, а только кодируется в base64url. Не кладите туда пароли, ключи или персональные данные. Подпись лишь гарантирует, что токен не был изменён.

    Подготовка проекта

    Установим зависимости:

    npm install express jsonwebtoken bcrypt cookie-parser
    npm install --save-dev @types/jsonwebtoken @types/express typescript
    

    В переменные окружения вынесем секреты:

    JWT_ACCESS_SECRET=replace_with_long_random_string
    JWT_REFRESH_SECRET=another_long_random_string
    ACCESS_TTL=15m
    REFRESH_TTL=30d
    

    Генерируйте секреты длиной не менее 32 байт, например через openssl rand -hex 32. Никогда не коммитьте их в репозиторий.

    Генерация access и refresh токенов

    Создадим сервис для работы с токенами:

    import jwt from 'jsonwebtoken';
    
    export interface TokenPayload {
      userId: string;
      email: string;
    }
    
    export class TokenService {
      // Создаём пару токенов на основе данных пользователя
      generateTokens(payload: TokenPayload) {
        const accessToken = jwt.sign(payload, process.env.JWT_ACCESS_SECRET!, {
          expiresIn: process.env.ACCESS_TTL,
        });
    
        const refreshToken = jwt.sign(payload, process.env.JWT_REFRESH_SECRET!, {
          expiresIn: process.env.REFRESH_TTL,
        });
    
        return { accessToken, refreshToken };
      }
    
      // Проверка access-токена: вернёт payload или null
      validateAccess(token: string): TokenPayload | null {
        try {
          return jwt.verify(token, process.env.JWT_ACCESS_SECRET!) as TokenPayload;
        } catch {
          return null;
        }
      }
    
      validateRefresh(token: string): TokenPayload | null {
        try {
          return jwt.verify(token, process.env.JWT_REFRESH_SECRET!) as TokenPayload;
        } catch {
          return null;
        }
      }
    }
    

    Разные секреты для access и refresh — обязательное условие. Если использовать один ключ, скомпрометированный refresh даст и доступ к ресурсам, и возможность бесконечно его обновлять.

    Эндпоинты логина и обновления

    При логине проверяем пароль через bcrypt и выдаём пару токенов. Refresh кладём в httpOnly cookie, access возвращаем в теле ответа:

    import { Router } from 'express';
    import bcrypt from 'bcrypt';
    
    const router = Router();
    const tokens = new TokenService();
    
    router.post('/login', async (req, res) => {
      const { email, password } = req.body;
      const user = await userRepository.findByEmail(email);
    
      if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
        return res.status(401).json({ message: 'Неверные учётные данные' });
      }
    
      const pair = tokens.generateTokens({ userId: user.id, email: user.email });
      await refreshRepository.save(user.id, pair.refreshToken);
    
      // httpOnly защищает refresh от чтения через JavaScript
      res.cookie('refreshToken', pair.refreshToken, {
        httpOnly: true,
        secure: true,
        sameSite: 'strict',
        maxAge: 30 * 24 * 60 * 60 * 1000,
      });
    
      res.json({ accessToken: pair.accessToken });
    });
    

    Отдельный эндпоинт /refresh принимает refresh из cookie, проверяет его наличие в БД (ротация) и выдаёт новую пару:

    router.post('/refresh', async (req, res) => {
      const token = req.cookies.refreshToken;
      const payload = tokens.validateRefresh(token);
      const stored = await refreshRepository.find(token);
    
      if (!payload || !stored) {
        return res.status(401).json({ message: 'Refresh недействителен' });
      }
    
      // Удаляем старый токен, чтобы исключить повторное использование
      await refreshRepository.delete(token);
      const pair = tokens.generateTokens({ userId: payload.userId, email: payload.email });
      await refreshRepository.save(payload.userId, pair.refreshToken);
    
      res.cookie('refreshToken', pair.refreshToken, { httpOnly: true, secure: true, sameSite: 'strict' });
      res.json({ accessToken: pair.accessToken });
    });
    

    Middleware для защищённых маршрутов

    export function authGuard(req, res, next) {
      const header = req.headers.authorization;
      if (!header?.startsWith('Bearer ')) {
        return res.status(401).json({ message: 'Нет токена' });
      }
    
      const payload = tokens.validateAccess(header.slice(7));
      if (!payload) {
        return res.status(401).json({ message: 'Токен истёк или повреждён' });
      }
    
      req.user = payload;
      next();
    }
    

    Частые ошибки

    • Хранение refresh в localStorage. Любой XSS моментально украдёт токен. Используйте httpOnly cookie с флагами Secure и SameSite.
    • Одинаковый секрет для access и refresh. Усложняет ротацию и снижает безопасность.
    • Отсутствие ротации refresh. Без удаления старого токена украденный refresh работает бессрочно.
    • Чувствительные данные в payload. Помните, что payload читается без ключа.
    • Слишком длинный TTL access. Делайте его коротким: 5–15 минут.
    • Игнорирование алгоритма none. Всегда указывайте конкретный алгоритм при verify через параметр algorithms.

    Заключение

    Схема access + refresh даёт баланс между удобством пользователя и безопасностью. Короткий access ограничивает урон от утечки, refresh с ротацией и httpOnly cookie защищает долгоживущую сессию. Главное — хранить секреты в env, использовать разные ключи для двух токенов и обязательно вести таблицу выданных refresh для отзыва. Такой подход легко масштабируется на микросервисы и подходит для большинства production-задач на Node.js.

    Иконка глаза605

    Комментарии

    0

    Постройте личный план изучения Neovim - практика и настройка до уровня Middle — бесплатно!

    Neovim - практика и настройка — часть карты развития Frontend, Backend, DevOps

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

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

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

    React и Redux Toolkit

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

    Zustand

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

    Next.js - с нуля

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

    Похожие статьи

    Картинка поста Как установить Node.js на Windows: пошаговая инструкция
    Иконка аватараАнтон
    Иконка календаря17 июля 2026
    Node.jsWindowsnpm+ 2juniorИконка уровня junior

    Как установить Node.js на Windows: пошаговая инструкция

    Как установить Node.js на Windows — пошаговая инструкция через официальный установщик и nvm-windows с разбором частых ошибок.

    Иконка чипа0
    Иконка глаза699
    Иконка комментариев0
    Картинка поста CI/CD с GitHub Actions: автоматизация деплоя для начинающих
    Иконка аватараАнтон
    Иконка календаря03 июля 2026
    CI/CDGitHub ActionsDevOps+ 2juniorИконка уровня junior

    CI/CD с GitHub Actions: автоматизация деплоя для начинающих

    CI/CD с GitHub Actions: пошаговая настройка pipeline для автоматического тестирования и деплоя Node.js-приложений без сторонних сервисов.

    Иконка чипа0
    Иконка глаза619
    Иконка комментариев0
    Картинка поста CI/CD: что это такое и как настроить пайплайн с нуля
    Иконка аватараАнтон
    Иконка календаря21 августа 2026
    CI/CDDevOpsGitHub Actions+ 2middleИконка уровня middle

    CI/CD: что это такое и как настроить пайплайн с нуля

    CI/CD — что это простыми словами и как настроить автоматическую сборку, тестирование и деплой кода на примере GitHub Actions и GitLab CI.

    Иконка чипа0
    Иконка глаза123
    Иконка комментариев0
    Иконка чипа0