Истечение срока действия токена — один из базовых механизмов
безопасности JWT, реализованный через стандартное поле exp
(expiration time). Это числовое значение в формате UNIX timestamp
(секунды с 1970-01-01), после которого токен считается недействительным
независимо от его подписи.
В библиотеке jose проверка срока жизни токена происходит
автоматически при верификации JWT. При обнаружении истёкшего
exp выбрасывается ошибка JWTExpired, которая
является частью стандартного набора ошибок проверки токена.
При вызове jwtVerify библиотека выполняет несколько
последовательных шагов:
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 представляет собой специализированный класс
ошибки, который позволяет различать причины отказа валидации токена. Это
важно, поскольку истечение срока — это не ошибка подписи и не нарушение
структуры, а ожидаемое состояние жизненного цикла токена.
Обработка выглядит следующим образом:
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('Другая ошибка валидации')
}
}
Такой подход позволяет разделять сценарии обработки:
Ошибка JWTExpired содержит дополнительную
информацию:
claim — указывает на поле expreason — описание причиныpayload (в некоторых сценариях может
отсутствовать)Это позволяет логировать точную причину отказа без дополнительного парсинга токена.
Одной из частых проблем в распределённых системах является
несинхронизированное время между клиентом и сервером. Для компенсации
используется параметр clockTolerance.
await jwtVerify(token, secret, {
clockTolerance: 30
})
Значение задаётся в секундах и позволяет считать токен действительным в пределах допустимого отклонения времени.
Механика проверки становится следующей:
valid = (currentTime - clockTolerance) <= exp
Это снижает вероятность ложных срабатываний JWTExpired
при незначительных расхождениях времени.
Поле exp отвечает за окончание срока действия токена,
однако в jose также учитываются дополнительные временные claims:
nbf (not before) — токен не должен использоваться до
указанного времениiat (issued at) — время выпуска токена, используется
как опорное значениеПри валидации порядок проверки важен: сначала проверяется допустимость использования (nbf), затем истечение (exp).
Если nbf не пройден, токен может быть отклонён ещё до
проверки срока действия, но при истечении всегда приоритет имеет
JWTExpired.
В системах с access/refresh токенами JWTExpired
становится ключевым сигналом для запуска механизма обновления.
Типичный поток:
jwtVerifyJWTExpiredПример логики обработки:
try {
await jwtVerify(accessToken, secret)
} catch (err) {
if (err instanceof JWTExpired) {
// инициировать refresh flow
}
}
Важно, что истёкший токен не требует проверки подписи повторно — ошибка возникает уже после успешной криптографической валидации.
Помимо exp, jose поддерживает параметр
maxTokenAge, который ограничивает возраст токена
относительно iat.
await jwtVerify(token, secret, {
maxTokenAge: '2h'
})
В этом случае даже при корректном exp токен будет
считаться недействительным, если он слишком старый.
Это добавляет дополнительный уровень контроля, особенно в системах,
где exp может быть слишком длинным или
неконтролируемым.
Если токен не содержит exp, поведение зависит от
конфигурации:
Отсутствие exp обычно рассматривается как нарушение best
practices JWT и повышает риск неконтролируемого доступа.
Общая модель обработки ошибок jose строится вокруг классификации исключений:
JWTExpired — срок действия завершёнJWTInvalid — общая ошибка структуры или claimsJWSInvalid — проблема подписиТакое разделение позволяет строить предсказуемую систему реакций без анализа строк ошибок.
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 фиксирует момент, когда
криптографически корректный токен перестаёт быть допустимым для
авторизации.
Именно поэтому корректная обработка этой ошибки является обязательной частью любой системы, использующей jose для работы с JWT.