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

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

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • 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.

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

    Комментарии

    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 ₽
    Подробнее
    изображение курса

    React и Redux Toolkit

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

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

    Картинка поста WebSocket на Node.js: строим real-time чат с нуля за час
    Иконка аватараАнтон
    Иконка календаря26 мая 2026
    Node.jsWebSocketJavaScript+ 2middleИконка уровня middle

    WebSocket на Node.js: строим real-time чат с нуля за час

    WebSocket на Node.js: пошаговое руководство по созданию real-time чата с библиотекой ws, обработкой подключений и broadcast-сообщений.

    Иконка чипа0
    Иконка глаза72
    Иконка комментариев0
    Картинка поста REST API на Node.js и Express: пишем сервер с нуля за час
    Иконка аватараАнтон
    Иконка календаря08 мая 2026
    Node.jsExpressREST API+ 2juniorИконка уровня junior

    REST API на Node.js и Express: пишем сервер с нуля за час

    REST API на Node.js и Express: пошаговое руководство по созданию сервера с маршрутизацией, валидацией и обработкой ошибок.

    Иконка чипа0
    Иконка глаза272
    Иконка комментариев0
    Картинка поста Event Loop в Node.js: как работает и зачем это знать
    Иконка аватараАнтон
    Иконка календаря04 мая 2026
    Node.jsJavaScriptEvent Loop+ 2middleИконка уровня middle

    Event Loop в Node.js: как работает и зачем это знать

    Event Loop в Node.js — основа асинхронности. Разбираем фазы цикла событий, очереди задач и типичные ошибки на практических примерах.

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