JWTExpired: обработка истёкшего токена

Истечение срока действия токена — один из базовых механизмов безопасности JWT, реализованный через стандартное поле exp (expiration time). Это числовое значение в формате UNIX timestamp (секунды с 1970-01-01), после которого токен считается недействительным независимо от его подписи.

В библиотеке jose проверка срока жизни токена происходит автоматически при верификации JWT. При обнаружении истёкшего exp выбрасывается ошибка JWTExpired, которая является частью стандартного набора ошибок проверки токена.

При вызове jwtVerify библиотека выполняет несколько последовательных шагов:

  • проверка криптографической подписи (JWS)
  • проверка структуры токена
  • проверка стандартных claims (exp, nbf, iat)
  • применение дополнительных опций валидации

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

import { jwtVerify } from 'jose'

const token = 'eyJhbGciOi...'

const secret = new TextEncoder().encode('secret-key')

const { payload } = await jwtVerify(token, secret)

Если токен истёк, выполнение не вернёт payload. Вместо этого будет выброшена ошибка.

JWTExpired как тип ошибки

JWTExpired представляет собой специализированный класс ошибки, который позволяет различать причины отказа валидации токена. Это важно, поскольку истечение срока — это не ошибка подписи и не нарушение структуры, а ожидаемое состояние жизненного цикла токена.

Обработка выглядит следующим образом:

import { jwtVerify, errors } from 'jose'

const { JWTExpired } = errors

try {
  const { payload } = await jwtVerify(token, secret)
  console.log(payload)
} catch (err) {
  if (err instanceof JWTExpired) {
    console.log('Токен истёк')
  } else {
    console.log('Другая ошибка валидации')
  }
}

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

  • истёкший токен → инициирование обновления (refresh flow)
  • повреждённый токен → отказ в доступе
  • неверная подпись → потенциальная атака или подмена

Внутренняя структура ошибки

Ошибка JWTExpired содержит дополнительную информацию:

  • claim — указывает на поле exp
  • reason — описание причины
  • payload (в некоторых сценариях может отсутствовать)

Это позволяет логировать точную причину отказа без дополнительного парсинга токена.

Учёт расхождения времени (clock skew)

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

await jwtVerify(token, secret, {
  clockTolerance: 30
})

Значение задаётся в секундах и позволяет считать токен действительным в пределах допустимого отклонения времени.

Механика проверки становится следующей:

valid = (currentTime - clockTolerance) <= exp

Это снижает вероятность ложных срабатываний JWTExpired при незначительных расхождениях времени.

Различие exp, nbf и iat в контексте истечения

Поле exp отвечает за окончание срока действия токена, однако в jose также учитываются дополнительные временные claims:

  • nbf (not before) — токен не должен использоваться до указанного времени
  • iat (issued at) — время выпуска токена, используется как опорное значение

При валидации порядок проверки важен: сначала проверяется допустимость использования (nbf), затем истечение (exp).

Если nbf не пройден, токен может быть отклонён ещё до проверки срока действия, но при истечении всегда приоритет имеет JWTExpired.

Обработка истёкшего токена в архитектуре аутентификации

В системах с access/refresh токенами JWTExpired становится ключевым сигналом для запуска механизма обновления.

Типичный поток:

  1. клиент отправляет access token
  2. сервер вызывает jwtVerify
  3. выбрасывается JWTExpired
  4. система инициирует проверку refresh token
  5. при успехе выдаётся новый access token

Пример логики обработки:

try {
  await jwtVerify(accessToken, secret)
} catch (err) {
  if (err instanceof JWTExpired) {
    // инициировать refresh flow
  }
}

Важно, что истёкший токен не требует проверки подписи повторно — ошибка возникает уже после успешной криптографической валидации.

maxTokenAge и альтернативный контроль времени жизни

Помимо exp, jose поддерживает параметр maxTokenAge, который ограничивает возраст токена относительно iat.

await jwtVerify(token, secret, {
  maxTokenAge: '2h'
})

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

Это добавляет дополнительный уровень контроля, особенно в системах, где exp может быть слишком длинным или неконтролируемым.

Поведение при отсутствии exp

Если токен не содержит exp, поведение зависит от конфигурации:

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

Отсутствие exp обычно рассматривается как нарушение best practices JWT и повышает риск неконтролируемого доступа.

Практическая модель обработки JWTExpired

Общая модель обработки ошибок jose строится вокруг классификации исключений:

  • JWTExpired — срок действия завершён
  • JWTInvalid — общая ошибка структуры или claims
  • JWSInvalid — проблема подписи

Такое разделение позволяет строить предсказуемую систему реакций без анализа строк ошибок.

import { errors } from 'jose'

const { JWTExpired, JWTInvalid } = errors

function handleAuthError(err) {
  switch (true) {
    case err instanceof JWTExpired:
      return 'expired'
    case err instanceof JWTInvalid:
      return 'invalid'
    default:
      return 'unknown'
  }
}

Подобная классификация упрощает интеграцию с middleware уровня API и системами контроля доступа.

Особенности поведения при асинхронной проверке

Так как jwtVerify является асинхронной операцией, JWTExpired возникает в виде rejected promise. Это требует обязательного использования try/catch или .catch().

jwtVerify(token, secret)
  .then(({ payload }) => {
    console.log(payload)
  })
  .catch(err => {
    if (err.name === 'JWTExpired') {
      console.log('expired')
    }
  })

Игнорирование обработки приводит к неконтролируемому прерыванию цепочки выполнения.

Ключевая роль JWTExpired в безопасности

Истечение токена — это не ошибка, а штатный механизм ограничения времени доверия. JWTExpired фиксирует момент, когда криптографически корректный токен перестаёт быть допустимым для авторизации.

Именно поэтому корректная обработка этой ошибки является обязательной частью любой системы, использующей jose для работы с JWT.