Сообщение об ошибке
jwt expired
Что это означает
TokenExpiredError из jsonwebtoken: утверждение exp токена (метка времени Unix в секундах) раньше текущего времени по часам проверяющей стороны. Сама подпись при этом может быть совершенно верной — токен просто пережил срок жизни, который задал ему издатель. В объекте ошибки есть поле expiredAt с точным моментом истечения.
Почему это происходит
Срок действия токена действительно истёк
частоКороткоживущие токены доступа (5–60 минут) — норма; клиент, который кеширует токен и использует его позже, или фоновая задача, которая держит один токен на протяжении долгой работы, получат эту ошибку точно по расписанию. Решение — обновление токена (refresh flow), а не более долгий срок жизни.
Расхождение часов издателя и проверяющей стороны
частоЕсли часы проверяющего сервера спешат относительно часов издателя, токены «истекают» раньше срока — иногда в момент выпуска. Часы контейнеров и виртуальных машин без NTP уходят на удивление быстро; расхождения в 2 минуты достаточно, чтобы токены со сроком жизни меньше часа время от времени отклонялись.
exp записан не в тех единицах
иногдаСпецификация (RFC 7519) задаёт exp в секундах. Код, который делит на 1000 на один раз больше, чем нужно, получает метки времени в 1970 году, и каждый токен сразу оказывается просроченным. Обратный промах — запись в exp значения Date.now() (в миллисекундах) — эту ошибку вообще не вызывает: метки времени оказываются больше чем на 56 000 лет в будущем, и срок действия токена не истекает никогда.
Как исправить
- 1.
Явно разрешите небольшое расхождение часов
clockTolerance задаётся в секундах и действует на exp и nbf. Делайте его небольшим (30–60 с) — он нужен, чтобы компенсировать уход часов, а не чтобы продлевать срок жизни токенов.
js jwt.verify(token, publicKey, { algorithms: ["ES256"], clockTolerance: 30, // seconds of tolerated skew }); - 2.
Проверьте exp, прежде чем винить часы
Декодируйте утверждения и выведите exp рядом с текущим временем. Если exp выглядит как 1.7e12, у вас ошибка с миллисекундами; если exp указывает на момент несколько минут назад, нужна логика обновления токена.
js const { exp } = jwt.decode(token); console.log("exp:", new Date(exp * 1000).toISOString()); console.log("now:", new Date().toISOString()); - 3.
Синхронизируйте часы и добавьте обновление токена
Запустите NTP (chrony/systemd-timesyncd) на хостах, которые проверяют токены, и реализуйте на стороне клиента обновление токена при ответах 401. Никогда не выкатывайте ignoreExpiration: true в рабочую среду — так каждый утёкший токен превращается в бессрочные учётные данные.