APIが401を返したとき、Authorization: Bearer eyJ... をコピーして、検索上位のJWTデコーダに貼り付けて exp を見る。よくある流れですが、そのトークンは有効な認証情報そのものです。相手のサーバーでログに残れば、期限が切れるまで誰でも使えてしまいます。
JWTは暗号化されていない
JWTは header.payload.signature の3つをドットでつないだものです。headerとpayloadはbase64urlエンコードされたJSONで、暗号化はされていません。つまりデコードにサーバーは不要です。
ブラウザのコンソールならこれだけです。
const [h, p] = token.split('.');
const dec = s => JSON.parse(atob(s.replace(/-/g, '+').replace(/_/g, '/')));
console.log(dec(h), dec(p));
ブラウザ内で完結するツール
タイムスタンプを読みやすく表示したり、署名検証まで行いたいときは、NibboのJWTデコードを使っています。デコードはページ内のJavaScriptで、署名検証はブラウザ標準のWebCrypto APIで行われます。HS256/384/512は共有シークレット、RS・PS・ES系はPEMまたはJWK形式の公開鍵に対応しています。DevToolsのNetworkタブを開いておけば、トークンが外部に送信されていないことを自分で確認できます。
401の原因になりやすいポイント
-
expが過去:exp・iat・nbfはUTCのUnix秒です。JSTの感覚で見ると9時間ずれて混乱しがちです。 -
nbfが未来:発行側とAPI側のサーバー時刻のずれ。ライブラリのleeway設定で30〜60秒の余裕を持たせます。 -
audの不一致:ステージング用に発行したトークンで本番APIを叩いている。 -
issの不一致:IdPのテナントURLが末尾のスラッシュやリージョンで違う。 -
algの不一致:サーバー側はトークンが名乗るalgを信用せず、固定すること。 -
鍵ローテーション:headerの
kidに対応する鍵を、APIがまだJWKSから取得していない。
周辺の便利ツール
Basic認証ヘッダやWebhookの本文など、JWT以外にもbase64はよく出てきます。Base64エンコード/デコードやJSON整形も、同じくブラウザ内で処理されるので、社外秘のデータでも安心して使えます。
payloadに入れてはいけないもの
誰でも読めるので、パスワード、APIキー、必要以上の個人情報は入れないこと。トークンの有効期限は短くし、長期のセッションはリフレッシュトークンで扱いましょう。
まとめ
JWTのデコードはただのbase64です。ローカルでデコードし、署名はWebCryptoで検証し、exp/nbf/aud/iss/alg/kid を順に確認すれば、ほとんどの401は解決します。
Nibbo作者 Vitalii