エラーメッセージ
jwt expired
このエラーの意味
jsonwebtoken の TokenExpiredError です。トークンの exp クレーム(秒単位の Unix タイムスタンプ)が、検証者の現在時刻より前になっています。署名自体はまったく問題なく有効かもしれません。トークンが、発行者の定めた有効期間を過ぎただけです。エラーオブジェクトの expiredAt には、期限切れになった正確な時刻が入っています。
発生する理由
トークンが本当に期限切れになっている
よくある有効期間の短いアクセストークン(5〜60 分)が一般的です。トークンをキャッシュして後で再利用するクライアントや、長時間の処理の間ずっと同じトークンを持ち続けるバックグラウンドジョブは、まさに予定どおりのタイミングでこのエラーに遭遇します。対処法は、有効期間を延ばすことではなく、リフレッシュのフローを用意することです。
発行者と検証者の間の時刻のずれ
よくある検証するサーバーの時計が発行者の時計より進んでいると、トークンが早く「期限切れ」になります。発行された瞬間に期限切れになることさえあります。NTP を使っていないコンテナーや VM は、驚くほど速く時刻がずれます。2 分のずれがあるだけで、有効期間が 1 時間未満のトークンが断続的に失敗するようになります。
exp の単位が間違っている
ときどき仕様(RFC 7519)では、exp は秒単位で定義されています。1000 で割る処理を 1 回多く行うコードは 1970 年のタイムスタンプを生成し、すべてのトークンが即座に期限切れになります。逆の間違い、つまり Date.now()(ミリ秒)を exp に書き込んだ場合は、このエラーはまったく発生しません。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 のような値ならミリ秒のバグです。数分前の時刻なら、リフレッシュのロジックが必要です。
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 を決して本番環境にリリースしてはいけません。漏洩したトークンがすべて、永続的な認証情報になってしまいます。