エラーメッセージ
"exp" claim timestamp check failed
jose (JavaScript) — JWTExpired, code ERR_JWT_EXPIRED
このエラーの意味
jose のクレーム検証は、exp のタイムスタンプが検証者の時計から見て過去であるため、トークンを拒否しました。署名の検証はすでに通過しており、これは純粋に鮮度の問題です。トークンが利用開始時刻より前に使われた場合など、ほかのクレームについても jose は同じパターンの文言(「"nbf" claim timestamp check failed」)を出します。
発生する理由
トークンが有効期間を過ぎて使われている
よくあるアクセストークンは短命になるよう設計されています。有効期限を確認せずにトークンをキャッシュするクライアント、トークンを含むメッセージを、キューに入れられてから何時間も後に処理するコンシューマー、トークンをハードコードしたテストフィクスチャは、いずれも確実にこのエラーになります。
検証者の時計が発行者の時計より進んでいる
よくある検証するホストで時計が数分ずれると、すべてのトークンの実質的な有効期間が短くなり、発行されたばかりのトークンが拒否されることもあります。コンテナーはホストの時計を引き継ぐため、時計のずれた VM があると、その上のすべての Pod が一斉に失敗します。
exp に誤った単位で値を書き込んでいる
ときどきexp はエポックからの秒数でなければなりません(RFC 7519)。割り算を誤ったり、ミリ秒としてシリアライズされる Date オブジェクトを渡したりする発行側のコードは、発行時点で期限切れのトークンか、何千年も有効なトークンを生成します。
修正方法
- 1.
上限を決めて時刻の許容誤差を設ける
jose は clockTolerance オプション(秒数、または人が読める形式の文字列)を受け付けます。値は小さく保ってください。これは時計のずれを吸収するためのもので、有効期間を延ばすためのものではありません。
js import { jwtVerify } from "jose"; await jwtVerify(token, key, { algorithms: ["ES256"], clockTolerance: "30s", }); - 2.
何かを変更する前に時系列を出力する
iat、exp、検証者の現在時刻を比較してください。パターンから原因がわかります。exp が数秒前 → トークン更新の問題、exp が iat より前 → 発行側のバグ、exp ≈ 現在時刻なのに失敗 → 時刻のずれ。
js import { decodeJwt } from "jose"; const { iat, exp } = decodeJwt(token); console.log({ iat: new Date(iat * 1000), exp: new Date(exp * 1000), now: new Date() });