本文へスキップ
ecdsa.com
Node.js

jsonwebtoken (Node.js)

Auth0 の jsonwebtoken は、Node.js でインストール数が最も多い JWT パッケージです。node:crypto の上で HMAC、RSA、ECDSA のトークンを扱い、バージョン 9 以降はアルゴリズムのチェックが鍵の種類に結び付けられていますが、EdDSA には対応していません。
ライセンス
MIT
確認したバージョン
9.0.3 ·
ソースコード
github.com/auth0/node-jsonwebtoken
ドキュメント
github.com/auth0/node-jsonwebtoken#readme

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

jsonwebtoken は、jwt.sign()、jwt.verify()、jwt.decode() の 3 つの呼び出しで JSON Web Token の署名と検証を行います。JWS の層は jws パッケージと jwa パッケージで処理し、暗号処理は node:crypto で行うため、鍵には PEM 文字列、バッファー、KeyObject インスタンスを使えます。jwt.decode() はトークンを解析するだけで、署名は一切チェックしません。そのため、その結果からクレームを読み取るコードは、送信者が書いた内容を何でもそのまま信頼することになります。

バージョン 9 では典型的なアルゴリズム混同の経路が塞がれており、チェックでも 9.0.3 でそれを確認できました。algorithms オプションを指定しない場合、許可リストは鍵の種類から決まり、公開鍵のテキストを鍵とした HS256 トークンは拒否されます。また、alg: none が通るのは、none を列挙したうえで鍵をまったく渡さない場合だけです。それでも algorithms は指定してください。この指定は前提をコード上で明文化し、鍵の種類が将来変わっても機能し続けます。

対応するアルゴリズムは RFC 7518 の範囲までで、EdDSA、Ed25519、ES256K はありません。Ed25519 で署名する発行者(jose、Go、Rust のスタックでよく見られます)のトークンはここでは検証できないため、代わりに jose を使ってください。また、ES256 には P-256 の鍵が必要です。その名前で P-384 の鍵を使って署名すると、ほかのライブラリが拒否するようなトークンを生成するのではなく、例外がスローされます。

トークン内の ECDSA 署名は、JWS が義務付ける 64 バイトの生の r‖s 形式です。openssl dgst、Java の SHA256withECDSA、クラウドの KMS を使ってトークンを手作業で組み立てた場合、署名は DER になり、検証は "ES256" signatures must be "64" bytes で失敗します。トークンを JWT デバッガーに貼り付けて署名の長さを確認し、DER ⇄ 生形式の変換ツールで修正してください。

多くのチームが意外に思うデフォルトが 2 つあります。1 つは想定より厳しく、もう 1 つは想定より緩いものです。clockTolerance はゼロなので、サーバー間で時刻が数秒ずれただけで jwt expired になります。また、exp は任意なので、これを含めずに発行されたトークンは決して期限切れになりません。署名時には expiresIn を設定し、検証時には maxAge を設定するか exp を明示的に必須にしてください。HMAC シークレットは JWT シークレットチェッカーで確認してください。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/jsonwebtoken/run.sh がコンテナイメージ node:24.22.0-bookworm-slim 内で jsonwebtoken (Node.js) 9.0.3 を Node.js 24.22.0 で実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

    チェックでは、HS256/384/512、RS256/384/512、PS256/384/512、ES256/384/512 で署名と検証ができました。EdDSA、Ed25519、Ed448、ES256K、ML-DSA は署名時に拒否されました。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
    evidence jws-algorithms: ES256K: sign throws Error: "algorithm" must be a valid string enum value
    evidence jws-algorithms: EdDSA: sign throws Error: "algorithm" must be a valid string enum value
    evidence jws-algorithms: Ed25519: sign throws Error: "algorithm" must be a valid string enum value
    evidence jws-algorithms: Ed448: sign throws Error: "algorithm" must be a valid string enum value
    evidence jws-algorithms: ML-DSA-44: sign throws Error: "algorithm" must be a valid string enum value
  • ES256 署名のエンコーディング

    raw

    トークン内の ES256 署名は、RFC 7518 が求めるとおり 64 バイトの生の r‖s です。node:crypto が生成する DER からの変換はライブラリが行います。

    チェックによる記録
    fact jws-ecdsa-encoding = raw
  • JWS 内の DER 署名

    rejected

    ES256 署名を DER に再エンコードしたトークンは、"ES256" signatures must be "64" bytes, saw "72" で失敗します(数値は DER データの長さです)。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: TypeError: "ES256" signatures must be "64" bytes, saw "72"
  • アルゴリズムの許可リスト

    from-key

    algorithms を指定しない場合、検証では鍵に合うアルゴリズムだけが許可されます。HS256 トークンは共有シークレットで、ES256 トークンは EC 公開鍵で検証でき、EC 鍵に対する RS256 トークンは invalid algorithm で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: RS256 token against an EC key: JsonWebTokenError: invalid algorithm
  • 署名なしトークン(alg none)

    opt-in

    鍵を渡すと、署名のないトークンは常に jwt signature is required で拒否されます。検証を通るのは、algorithms に none が含まれ、鍵の引数が空の場合だけです。

    チェックによる記録
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with a key: JsonWebTokenError: jwt signature is required
    evidence jwt-alg-none: algorithms ["none"] and no key: verified
  • HMAC シークレットとして使われる公開鍵

    rejected

    公開鍵の PEM テキストを HMAC 鍵とした HS256 トークンは、アルゴリズムの一覧がない場合(invalid algorithm)も、algorithms: ["HS256"] を指定した場合(secretOrPublicKey must be a symmetric key when using HS256)も拒否されました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: no list: JsonWebTokenError: invalid algorithm
    evidence jwt-hmac-public-key: algorithms ["HS256"]: JsonWebTokenError: secretOrPublicKey must be a symmetric key when using HS256
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと "alg" parameter "ES256" requires curve "prime256v1". がスローされます。ES256、ES384、ES512 は、それぞれ P-256、P-384、P-521 に結び付けられています。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: Error: "alg" parameter "ES256" requires curve "prime256v1".
  • デフォルトのクロックスキュー

    0s

    デフォルトでは時刻のずれを許容しません。3 秒前に期限切れになったトークンは拒否されました。ずれを許容するには clockTolerance を秒単位で設定してください。

    チェックによる記録
    fact jwt-clock-skew = 0s
  • 有効期限クレームの要否

    no

    exp クレームのないトークンも検証を通りました。有効期限が適用されるのは、このクレームがある場合か maxAge を設定した場合だけです。

    チェックによる記録
    fact jwt-exp-required = no

使い方

ES256 トークンを発行して検証し、検証側でアルゴリズム、発行者、オーディエンスを固定します。
import { generateKeyPairSync } from "node:crypto";
import jwt from "jsonwebtoken";

const { privateKey, publicKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });

const token = jwt.sign({ sub: "user-42" }, privateKey, {
  algorithm: "ES256",
  expiresIn: "10m",
  issuer: "https://auth.example.com",
  audience: "api",
});

// Pin the algorithm, issuer and audience instead of trusting the token header.
const claims = jwt.verify(token, publicKey, {
  algorithms: ["ES256"],
  issuer: "https://auth.example.com",
  audience: "api",
});

console.log("sub:", claims.sub);

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ node:24.22.0-bookworm-slim 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
npm init -y >/dev/null && npm install --no-audit --no-fund jsonwebtoken@9.0.3
コマンド
node usage.mjs
一致すべき出力(正規表現)
sub: user-42

ライブラリ