Сообщение об ошибке
"nbf" claim timestamp check failed
jose (JavaScript) — JWTClaimValidationFailed, code ERR_JWT_CLAIM_VALIDATION_FAILED
Что это означает
В токене есть утверждение `nbf` («not before», «не ранее») со временем, которое по часам проверяющей стороны ещё не наступило, поэтому jose отклоняет токен как ещё не действительный. Подпись и остальные утверждения при этом могут быть в полном порядке: ошибка касается только времени. Почти всегда это значит, что часы издателя и проверяющей стороны расходятся сильнее, чем позволяет заданный вами допуск (по умолчанию он нулевой).
Почему это происходит
Часы проверяющей стороны отстают от часов издателя
частоМногие издатели задают `nbf` равным времени выдачи. Если часы проверяющего хоста отстают хотя бы на несколько секунд, каждый только что выпущенный токен на это время оказывается «из будущего». Контейнеры и виртуальные машины наследуют часы хоста, поэтому один хост с уплывающими часами ломает сразу все сервисы на нём.
Токен используют сразу после выпуска, без допуска
частоТесты и межсервисные вызовы часто выпускают токен и отправляют его в пределах нескольких миллисекунд. Когда у `clockTolerance` значение по умолчанию (0 секунд), обычного расхождения часов двух машин достаточно, чтобы проверка время от времени не проходила.
Издатель намеренно задал nbf в будущем
иногдаНекоторые системы заранее выпускают токены, которые начинают действовать позже (задания по расписанию, отложенные ссылки). Если такой токен используют раньше времени, отказ правильный: вызывающая сторона должна дождаться `nbf`.
nbf записан в миллисекундах
редкоВремя в JWT — это секунды с начала эпохи (RFC 7519, NumericDate). Издатель, который записывает `Date.now()`, получает `nbf` примерно на 50 000 лет в будущем, и токен не становится действительным никогда.
Как исправить
- 1.
Задайте небольшой явный допуск на расхождение часов
`clockTolerance` принимает число секунд или строку с длительностью. Допуск в 30–60 секунд компенсирует реальный уход часов; это не способ принимать токены на несколько минут раньше срока.
js import { jwtVerify } from "jose"; const { payload } = await jwtVerify(token, jwks, { algorithms: ["ES256"], clockTolerance: "30s", // applies to nbf, exp and iat }); - 2.
Прежде чем менять конфигурацию, выведите временную шкалу
Сравните `nbf` с текущим временем проверяющей стороны. Разница в секунды указывает на расхождение часов, разница в часы — на ошибку издателя с часовым поясом или единицами измерения, а 13-значный `nbf` — на миллисекунды.
js import { decodeJwt } from "jose"; const { iat, nbf, exp } = decodeJwt(token); const now = Math.floor(Date.now() / 1000); console.log({ iat, nbf, exp, now, nbfInSeconds: nbf - now }); - 3.
Исправьте уплывающие часы
Запустите NTP (chrony или systemd-timesyncd) на каждом хосте, который выпускает или проверяет токены. Допуск скрывает небольшое расхождение часов; маскировать большое — не его задача.
bash timedatectl status # "System clock synchronized: yes" chronyc tracking # offset from the NTP reference date -u +%s # compare with the token's nbf