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 Собеседование
    • 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 Собеседование
    • 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.

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

    Комментарии

    0

    Постройте личный план изучения Основы React, React Router и Redux Toolkit до уровня Middle — бесплатно!

    Основы React, React Router и Redux Toolkit — часть карты развития Frontend, Mobile

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

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

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

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

    Основы разработки

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга5.0
    бесплатно
    Подробнее
    изображение курса

    React Native и Expo Router

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

    Основы Swift и iOS

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

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

    Картинка поста Авторизация и аутентификация в Node.js: JWT, OAuth2 и сессии
    Иконка аватараАнтон
    Иконка календаря11 июня 2026
    Node.jsБезопасностьJWT+ 2middleИконка уровня middle

    Авторизация и аутентификация в Node.js: JWT, OAuth2 и сессии

    Авторизация и аутентификация на Node.js: разбираем JWT, OAuth2 и серверные сессии с примерами кода на Express и рекомендациями по безопасности.

    Иконка чипа0
    Иконка глаза195
    Иконка комментариев0
    Картинка поста WebSocket на Node.js: пишем чат и real-time уведомления
    Иконка аватараАнтон
    Иконка календаря09 июня 2026
    WebSocketNode.jsReal-time+ 1middleИконка уровня middle

    WebSocket на Node.js: пишем чат и real-time уведомления

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

    Иконка чипа+1
    Иконка глаза127
    Иконка комментариев0
    Картинка поста Архитектура Node.js: слои, модули и Dependency Injection
    Иконка аватараАнтон
    Иконка календаря08 июня 2026
    Node.jsАрхитектураBackend+ 1middleИконка уровня middle

    Архитектура Node.js: слои, модули и Dependency Injection

    Архитектура Node.js приложения: разбираем слоистую структуру, разделение на модули и применение Dependency Injection для масштабируемого кода.

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