エラーメッセージ
"nbf" claim timestamp check failed
jose (JavaScript) — JWTClaimValidationFailed, code ERR_JWT_CLAIM_VALIDATION_FAILED
このエラーの意味
トークンの `nbf`(not before)クレームが、検証者の時計ではまだ未来の時刻であるため、jose はトークンをまだ有効ではないとして拒否しました。署名やその他のクレームにはまったく問題がない可能性があり、このエラーは時刻だけに関するものです。ほとんどの場合、発行者と検証者の時計のずれが、許容する範囲(デフォルトはゼロ)を超えていることを意味します。
発生する理由
検証者の時計が発行者の時計より遅れている
よくある多くの発行者は、`nbf` を発行時刻と同じ値に設定します。検証するホストの時計が数秒でも遅れていると、発行されたばかりのトークンは、その時間だけすべて「未来のもの」になります。コンテナーや VM はホストの時計を引き継ぐため、時計のずれたホストが 1 台あると、その上のすべてのサービスが一斉に動かなくなります。
許容誤差なしで、発行直後にトークンを使っている
よくあるテストやサービス間の呼び出しでは、トークンを発行して数ミリ秒以内に送信することがよくあります。`clockTolerance` がデフォルトの 0 秒のままだと、2 台のマシン間の通常の時刻のずれだけで、断続的に失敗します。
発行者が意図的に nbf を未来に設定している
ときどきシステムによっては、後から有効になるトークンを事前に発行します(スケジュールされたジョブ、時間差で有効になるリンクなど)。そのようなトークンを早く使えば、拒否されるのが正しい動作です。呼び出し側は `nbf` まで待たなければなりません。
nbf がミリ秒で書き込まれている
まれJWT の時刻はエポックからの秒数です(RFC 7519 の NumericDate)。`Date.now()` を書き込む発行者は約 50,000 年後の `nbf` を生成するため、そのトークンが有効になることはありません。
修正方法
- 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` を検証者の現在時刻と比較してください。差が数秒なら時刻のずれ、数時間なら発行者側のタイムゾーンか単位のバグ、`nbf` が 13 桁ならミリ秒が原因です。
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