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.

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

    Комментарии

    0

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

    JavaScript с нуля - основы языка и практика для начинающих — часть карты развития Frontend, Backend, Mobile

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

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

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

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

    Продвинутый JavaScript

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

    TypeScript с нуля

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

    Neovim

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

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

    Иконка аватараАнтон
    Иконка календаря27 сентября 2026
    openaiAI-инструментыDevOps+ 2middleИконка уровня middle

    Как установить Codex на Windows в России

    Установка Codex на Windows состоит из двух разных дел, и второе обычно застаёт врасплох. Сам агент ставится одной командой `npm install -g @openai/codex` и на Windows работает так же, как на macOS: пакет скачивается из npm без всяких препятствий. А вот первый же запрос к модели закончится отказом: по умолчанию Codex авторизуется через аккаунт ChatGPT, а этот путь из России закрыт — ни вход, ни оплата подписки российской картой не пройдут. Поэтому после установки нужен второй шаг: прописать Codex другой адрес модельного провайдера и ключ к нему. Ключ можно взять в [AI для кода от PurpleSchool](

    Иконка чипа0
    Иконка глаза235
    Иконка комментариев0
    Иконка аватараАнтон
    Иконка календаря24 сентября 2026
    Claude CodeAnthropicAI-инструменты+ 2middleИконка уровня middle

    Как установить Claude Code на Windows: инструкция из России

    Claude Code ставится на Windows одной командой `npm install -g @anthropic-ai/claude-code` — при условии, что в системе есть Node.js. Проблема начинается после установки: CLI поднимется, но при первом же запросе вернёт ошибку доступа, потому что API Anthropic не обслуживает запросы из России. Поэтому установка на Windows из России — это два отдельных дела: поставить сам пакет и подсунуть ему рабочий адрес API с ключом. Ниже — оба шага по порядку, с синтаксисом именно для PowerShell, а не для bash.

    Иконка чипа0
    Иконка глаза564
    Иконка комментариев0
    Картинка поста Распределенные системы роутинга на примере Framework Foton
    Иконка аватараКонстантин
    Иконка календаря09 октября 2026
    распределениероутингfotonseniorИконка уровня senior

    Распределенные системы роутинга на примере Framework Foton

    Распределенные системы не только снижают нагрузку, но и формируют более прозрачную, тонкую и гибку архитектуру в монолитном приложении

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