Что хранить в токене, а что — в базе данных

В системах, использующих @hapi/iron, токен представляет собой защищённый контейнер для данных, который может быть проверен и расшифрован только при наличии корректного ключа. В отличие от классического JWT, Iron делает акцент не только на подписи, но и на шифровании содержимого, что меняет подход к проектированию данных внутри токена.

Основное правило проектирования: токен — это транспортный механизм, а не хранилище состояния приложения.

Любая информация, помещённая внутрь токена, становится:

  • потенциально доступной на клиенте (после расшифровки в приложении)
  • ограниченной по размеру
  • трудно обновляемой без перевыпуска

Поэтому выбор между токеном и базой данных — это выбор между временной, переносимой идентификацией и централизованным, изменяемым состоянием.


Что допустимо хранить в Iron-токене

Токен должен содержать минимальный набор данных, необходимый для идентификации запроса и базовой авторизации.

1. Идентификатор пользователя

Наиболее безопасный и распространённый вариант:

  • userId
  • sub (subject)
const payload = {
  userId: 42
};

Этот идентификатор используется как ключ для дальнейшего обращения к базе данных.


2. Время жизни токена

Любой токен должен иметь ограниченный срок действия:

  • exp (expiration time)
  • iat (issued at)

В Iron эти параметры часто добавляются автоматически, но концептуально они всегда должны присутствовать.

const payload = {
  userId: 42,
  exp: Date.now() + 1000 * 60 * 15
};

3. Минимальные атрибуты авторизации

Допустимо хранить только те данные, которые не требуют мгновенного обновления:

  • базовая роль (role: "admin" | "user")
  • уровень доступа
  • флаги состояния (например, isVerified)
const payload = {
  userId: 42,
  role: "admin"
};

Важно учитывать, что любые изменения ролей не вступят в силу до перевыпуска токена.


4. Идентификаторы сессии или контекста

Иногда добавляется:

  • sessionId
  • deviceId

Это позволяет связать токен с серверной сессией или конкретным устройством.

const payload = {
  userId: 42,
  sessionId: "a8f3c1"
};

Что категорически нельзя хранить в токене

Iron шифрует данные, но это не отменяет фундаментального принципа: токен может быть украден, сохранён или переиспользован.

1. Чувствительные персональные данные

Нельзя помещать:

  • пароли (даже хэшированные)
  • email в качестве основного идентификатора безопасности
  • номера документов
  • телефоны
  • адреса

Даже при шифровании это создаёт ненужные риски и усложняет ротацию данных.


2. Часто изменяющееся состояние

Любая информация, которая может измениться без перевыпуска токена:

  • баланс
  • подписки
  • лимиты
  • статусы заказов
  • права доступа в реальном времени

Причина: токен становится неактуальным сразу после изменения данных в системе.


3. Большие объёмы данных

Iron-токен передаётся в каждом запросе. Поэтому нельзя хранить:

  • списки сущностей
  • массивы разрешений
  • профили пользователя
  • результаты вычислений

Это приводит к:

  • росту latency
  • увеличению размера заголовков HTTP
  • деградации производительности API

Роль базы данных в архитектуре

Если токен отвечает за идентификацию, то база данных отвечает за истинное состояние системы.

База данных должна хранить:

1. Полный профиль пользователя

id | name | email | hashed_password | created_at

Все изменяемые атрибуты пользователя находятся здесь.


2. Права доступа и роли

Вместо хранения ролей в токене лучше использовать таблицы:

  • roles
  • permissions
  • user_roles

Это позволяет менять доступ без перевыпуска токена.


3. Состояние сессий

Если требуется контроль сессий:

  • активные сессии
  • устройства
  • IP-адреса
  • токены refresh-механизма

4. Бизнес-данные

Любая предметная область:

  • заказы
  • платежи
  • сообщения
  • документы

Баланс между токеном и базой данных

Архитектурно система строится вокруг принципа:

токен содержит ссылку на данные, база данных содержит сами данные

Пример потока запроса

  1. Клиент отправляет Iron-токен
  2. Сервер расшифровывает токен
  3. Извлекается userId
  4. По userId происходит запрос в базу
  5. База возвращает актуальное состояние пользователя
  6. Сервер применяет бизнес-логику

Почему нельзя дублировать данные

Дублирование информации между токеном и базой приводит к рассинхронизации.

Пример проблемы:

  • в токене роль: "user"
  • в базе роль обновлена до "admin"

Система будет вести себя неконсистентно до перевыпуска токена.


Особенности Iron по сравнению с JWT

Iron отличается тем, что:

  • данные шифруются, а не только подписываются
  • содержимое токена недоступно без ключа
  • токен ближе к зашифрованному контейнеру, чем к открытому JSON

Это создаёт иллюзию безопасности, но не отменяет архитектурных ограничений:

  • нельзя переносить бизнес-логику в токен
  • нельзя делать токен источником истины
  • нельзя хранить изменяемые сущности

Практическая модель разделения ответственности

В токене

  • userId
  • exp
  • sessionId
  • минимальная роль (опционально)

В базе данных

  • пользователь
  • роли и права
  • настройки
  • бизнес-данные
  • состояние системы

Ошибки проектирования, которые приводят к проблемам

Избыточное наполнение токена

Чем больше данных в токене, тем сложнее:

  • инвалидировать доступ
  • синхронизировать изменения
  • масштабировать систему

Использование токена как кэша

Токен не предназначен для хранения кэшированных данных пользователя. Это приводит к:

  • устареванию информации
  • сложной логике обновления
  • дублированию источников истины

Отсутствие серверной проверки

Полное доверие токену без обращения к базе данных допустимо только в крайне ограниченных сценариях. В реальных системах это приводит к:

  • невозможности отзыва прав
  • уязвимостям при компрометации токена

Архитектурный принцип

Любая система, использующая Iron, должна придерживаться разделения:

  • токен — идентификатор и транспорт
  • база данных — состояние и правила
  • сервер — место принятия решений