エラーメッセージ
secretOrPublicKey must be an asymmetric key when using ES256
このエラーの意味
jsonwebtoken に ES256 のトークンの検証(または署名。署名では対になるメッセージが secretOrPrivateKey になります)を指示しましたが、渡した鍵素材は共通鍵のシークレット、つまり PEM/KeyObject ではない単なる文字列や Buffer です。ES256 は P-256 上の ECDSA なので、共有シークレットではなく EC の鍵ペアが必要です。
発生する理由
HMAC 時代のシークレットが設定に残っている
よくあるサービスは共有シークレットを使う HS256 を使っていましたが、誰かが alg を ES256 に切り替え、JWT_SECRET には「supersecret」が入ったままです。ランダムな文字列は EC 鍵ではなく、jsonwebtoken はそれを EC 鍵として扱うことを拒否します。
誤った設定変数を参照している
よくあるPEM は存在しますが、コードが読み込んでいる変数には別のもの、つまり鍵 ID や JWKS の URL が入っています(https://issuer/.well-known/jwks.json は文字列であり、鍵ではありません)。変数が空の場合は、「secret or public key must be provided」という別のエラーとして報告されます。
設定を経由する途中で PEM が壊れた
よくあるリテラルの \n シーケンスを含んだまま届いた PEM、引用符で囲まれた PEM、YAML ファイルによってインデントされた PEM は、もう鍵として解析できません。そのため jsonwebtoken 9 はその文字列を共有シークレットとして扱い、このエラーを報告します。渡す前に改行を復元し、引用符を取り除いてください。
PEM/KeyObject が必要な場所に JWK を渡している
ときどきJWK は JSON オブジェクトです。これを文字列化して jwt.verify() に渡しても、PEM でも KeyObject でもない文字列になります。先に変換する必要があります(crypto.createPublicKey({ key: jwk, format: "jwk" }))。
修正方法
- 1.
本物の EC 公開鍵を読み込む
ES256 は PEM(SPKI)または KeyObject で検証してください。手元にあるのが JWK(たとえば JWKS エンドポイントから取得したもの)の場合は、先に node:crypto で変換してください。
js import { createPublicKey } from "node:crypto"; const key = createPublicKey({ key: jwk, format: "jwk" }); // or a PEM string jwt.verify(token, key, { algorithms: ["ES256"] }); - 2.
ヘルパーで JWKS の鍵を解決する
発行者が JWKS の URL で鍵を公開している場合は、その URL を鍵として渡さないでください。トークンごとに kid で鍵を解決してください。
js import { createRemoteJWKSet, jwtVerify } from "jose"; const jwks = createRemoteJWKSet(new URL("https://issuer.example/.well-known/jwks.json")); const { payload } = await jwtVerify(token, jwks, { algorithms: ["ES256"] }); - 3.
または:本当は HS256 を使いたかった場合
発行者と検証者がどちらも自社のサービスで、共有シークレットを意図的に使っている場合は、両側で ES256 ではなく algorithm: "HS256" を設定してください。2 つのファミリーの混在こそ、このエラーが防いでいるものです。古典的な鍵混同攻撃のシナリオも、これに含まれます。