Установка времени жизни токена: setExpirationTime

В библиотеке jose управление временем жизни токена реализуется через стандартное JWT-claim поле exp (expiration time). Оно определяет момент, после которого токен становится недействительным. В процессе формирования JWT при помощи SignJWT используется метод setExpirationTime, который добавляет это значение в payload и корректно кодирует его в соответствии со спецификацией JOSE.

Основная цель механизма — ограничить срок действия токена и минимизировать риск его повторного использования после компрометации.


Принцип работы поля exp

JWT содержит набор стандартных зарегистрированных claims. Среди них:

  • iat — время выпуска токена
  • nbf — время, с которого токен становится действительным
  • exp — время истечения срока действия

exp = , ;

Значение exp всегда выражается в Unix time (секунды с 1 января 1970 года UTC). При проверке токена библиотека сравнивает текущее серверное время с этим значением.

Если текущее время больше или равно exp, токен считается просроченным и отклоняется.


Использование setExpirationTime в SignJWT

В jose создание подписанного JWT выполняется через цепочку методов. Установка срока жизни реализуется методом:

import { SignJWT } from 'jose';

const token = await new SignJWT({ sub: 'user123' })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setExpirationTime('2h')
  .sign(secretKey);

Метод setExpirationTime принимает несколько форматов:

Строковые значения времени

Поддерживаются удобные относительные выражения:

  • '10s' — 10 секунд
  • '5m' — 5 минут
  • '2h' — 2 часа
  • '1d' — 1 день

Такие значения интерпретируются относительно времени выпуска токена (iat).


Абсолютное время через timestamp

Можно передать Unix timestamp:

.setExpirationTime(Math.floor(Date.now() / 1000) + 3600)

Здесь срок действия фиксируется явно, без зависимости от iat.


Использование Date

Допустим вариант с объектом Date:

.setExpirationTime(new Date('2026-01-01T00:00:00Z'))

Библиотека автоматически преобразует дату в Unix time.


Связь exp и iat

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

  • iat задаёт точку отсчёта
  • exp вычисляется относительно неё или задаётся явно

Если setIssuedAt() не указан, библиотека добавляет его автоматически при необходимости расчёта относительных значений времени.


Поведение при проверке токена

При валидации JWT используется jwtVerify:

import { jwtVerify } from 'jose';

const { payload } = await jwtVerify(token, secretKey);

Если exp меньше текущего времени, будет выброшено исключение JWTExpired.


Допуск времени (clock tolerance)

В распределённых системах возможны расхождения времени между серверами. Для компенсации используется параметр clockTolerance:

await jwtVerify(token, secretKey, {
  clockTolerance: '30s'
});

Это означает, что токен считается валидным ещё 30 секунд после истечения.


Практика выбора времени жизни токена

Срок действия зависит от типа токена и сценария использования:

Access token

Короткий срок жизни снижает риск злоупотребления:

  • 5–15 минут в высокозащищённых системах
  • до 1 часа в стандартных приложениях

Refresh token

Более длительный период:

  • от нескольких дней до месяцев

Частые ошибки при установке exp

1. Использование миллисекунд вместо секунд

JWT требует Unix time в секундах:

// Ошибка
.setExpirationTime(Date.now() + 3600)

// Правильно
.setExpirationTime(Math.floor(Date.now() / 1000) + 3600)

2. Несогласованность iat и exp

Если токен формируется с неверной логикой времени, например:

  • exp меньше iat
  • exp в прошлом

Такой токен сразу считается недействительным.


3. Использование слишком долгого срока жизни

Длительные access-токены увеличивают риск компрометации. Даже при наличии HTTPS и защиты каналов передачи, украденный токен может быть использован до истечения срока.


Поведение в различных алгоритмах подписи

Механизм setExpirationTime не зависит от алгоритма:

  • HS256
  • RS256
  • ES256

Во всех случаях exp обрабатывается одинаково, поскольку относится к payload, а не к криптографии подписи.


Влияние времени сервера

Корректность проверки exp полностью зависит от синхронизации времени:

  • использование NTP-синхронизации обязательно
  • рассинхронизация приводит к ложным ошибкам истечения

Комбинация с другими claims

На практике setExpirationTime часто используется вместе:

  • setIssuedAt() — фиксирует момент создания
  • setNotBefore() — задаёт задержку активации
  • пользовательскими claims (например, role, scope)

Пример:

const token = await new SignJWT({ role: 'admin' })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setNotBefore('0s')
  .setExpirationTime('15m')
  .sign(secretKey);

Влияние на архитектуру аутентификации

Короткоживущие токены приводят к необходимости:

  • использования refresh token механизма
  • обновления access token без повторной авторизации
  • централизованного контроля сессий

exp становится ключевым элементом стратегии stateless-аутентификации, где сервер не хранит состояние сессии.


Проверка корректности при генерации

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

  • exp > iat
  • разумный диапазон (например, не более 24 часов для access token)
  • соответствие политике безопасности системы

Ошибки на этом этапе невозможно исправить после выдачи токена, поскольку JWT неизменяем после подписи.