Обработка истёкших токенов

При работе с библиотеками уровня session encryption (в том числе Iron-based решениями) ключевым элементом является контроль времени жизни токена. Любой защищённый токен содержит метаданные, включающие timestamp создания и параметры TTL (time to live), которые используются при дешифрации для определения его актуальности.

При каждом запросе сервер выполняет декодирование зашифрованного значения и извлекает внутреннюю структуру:

  • идентификатор сессии
  • данные пользователя
  • время создания
  • срок действия (expiresAt или maxAge)

Далее выполняется сравнение текущего времени сервера с сохранёнными параметрами.

const isTokenExpired = (session) => {
  const now = Date.now();
  return now > session.expiresAt;
};

Если токен признан истёкшим, дальнейшая обработка сессии прекращается до этапа бизнес-логики.


Поведение системы при обнаружении истёкшего токена

После определения недействительности токена сервер обязан привести состояние к безопасному:

  • удаление сессионных данных из контекста запроса
  • очистка cookie на клиенте
  • возврат HTTP-статуса 401 Unauthorized или 440 Session Expired (в зависимости от архитектуры)
  • логирование события истечения токена для мониторинга

В типичной реализации middleware это выглядит как ранний выход из цепочки обработки:

export const sessionMiddleware = (options) => {
  return async (req, res, next) => {
    const session = await decryptSession(req.cookies.session);

    if (!session || isTokenExpired(session)) {
      res.setHeader('Set-Cookie', 'session=; Max-Age=0; Path=/; HttpOnly');
      return res.status(401).json({ error: 'Session expired' });
    }

    req.session = session;
    next();
  };
};

Стратегии обработки истечения токена

Существует несколько подходов к обработке просроченных токенов, выбор которых зависит от архитектуры приложения.

Полная инвалидизация сессии

Самый строгий вариант. При истечении срока действия пользователь полностью разлогинивается.

Используется в системах с повышенными требованиями безопасности, например:

  • административные панели
  • финансовые сервисы
  • системы с чувствительными данными

Недостаток — ухудшение пользовательского опыта.


Механизм refresh token

Более гибкая модель, при которой access token имеет короткое время жизни, а refresh token — длительное.

Алгоритм:

  1. access token истёк
  2. клиент отправляет refresh token
  3. сервер проверяет его валидность
  4. выдаётся новый access token
if (isAccessTokenExpired(token)) {
  const newToken = await refreshAccessToken(refreshToken);

  if (!newToken) {
    return res.status(401).send('Re-authentication required');
  }

  res.setHeader('Authorization', `Bearer ${newToken}`);
}

Обновление сессии при активном использовании

Часто применяется sliding expiration — продление срока жизни токена при активности пользователя.

Каждый валидный запрос обновляет expiresAt:

const extendSession = (session) => {
  session.expiresAt = Date.now() + session.maxAge;
  return session;
};

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


Обработка рассинхронизации времени

Проблема возникает при различии времени клиента и сервера. Это приводит к ложным срабатываниям истечения токена.

Для компенсации вводится допустимый буфер:

const CLOCK_SKEW = 60 * 1000; // 1 минута

const isTokenExpired = (session) => {
  return Date.now() - CLOCK_SKEW > session.expiresAt;
};

Это особенно важно в распределённых системах.


Безопасная инвалидация и защита от повторного использования

При истечении токена важно предотвращать его повторное использование:

  • добавление версии сессии (session versioning)
  • хранение blacklist для токенов (в случае JWT-подобных решений)
  • ротация refresh токенов при каждом обновлении

Пример проверки версии:

if (session.version !== user.currentSessionVersion) {
  throw new Error('Session invalidated');
}

Обработка ошибок на уровне клиента

Клиентская часть должна корректно реагировать на истёкший токен:

  • перехват 401 ответа
  • попытка refresh
  • редирект на страницу логина при неудаче
axios.interceptors.response.use(
  response => response,
  async (error) => {
    if (error.response.status === 401) {
      const refreshed = await tryRefreshToken();

      if (!refreshed) {
        window.location.href = '/login';
      }
    }
  }
);

Логирование и мониторинг истечения токенов

Системы продакшн-уровня фиксируют события истечения токенов для анализа:

  • частота истечений
  • время жизни сессий пользователей
  • аномальные паттерны (резкие массовые истечения)

Это позволяет выявлять:

  • ошибки конфигурации TTL
  • проблемы синхронизации времени
  • атаки с использованием украденных токенов

Оптимизация проверки токенов

При высокой нагрузке важно минимизировать стоимость операций:

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

В системах на основе Iron encryption проверка обычно происходит локально, без внешних запросов, что снижает задержки.


Поведение при частично повреждённых или некорректных токенах

Истёкший токен может сопровождаться повреждением структуры (обрезанные cookies, некорректное декодирование). В таких случаях применяется единый fallback:

  • игнорировать токен
  • считать сессию недействительной
  • не раскрывать детали ошибки клиенту
try {
  const session = decrypt(token);
  if (isTokenExpired(session)) throw new Error();
} catch {
  return null;
}