本文へスキップ
ecdsa.com
Go

golang-jwt/jwt

golang-jwt/jwt は、dgrijalva/jwt-go の後継としてコミュニティが保守する、Go で最も使われている JWT パッケージです。検証は鍵の型に基づいて行われ、none は明示的なセンチネル値を使ったときにだけ受け入れられ、有効期限は要求しない限り任意です。
ライセンス
MIT
確認したバージョン
5.3.1 ·
ソースコード
github.com/golang-jwt/jwt
ドキュメント
pkg.go.dev/github.com/golang-jwt/jwt/v5

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

github.com/golang-jwt/jwt/v5 は、標準ライブラリの鍵を使って JWT の解析、検証、署名を行います。HMAC には []byte を使い、ほかに *rsa.PublicKey、*ecdsa.PublicKey、ed25519.PublicKey を使います。5.3.1 は HS、RS、PS、ES の各ファミリーと EdDSA で署名と検証ができました。ES256K と、完全指定の Ed25519 と Ed448 という名前には、登録された署名メソッドがありません。

検証の要は、jwt.Parse に渡す keyfunc(鍵を返す関数)です。各署名メソッドは受け取った鍵の Go の型をチェックするため、*ecdsa.PublicKey を返す keyfunc では、RS256 トークンは RSA verify expects *rsa.PublicKey で失敗し、PEM のテキストを鍵とした HS256 トークンは HMAC verify expects []byte で失敗しました。keyfunc がどのトークンに対しても生のバイト列を返すと、この保護はなくなります。チェックでは、PEM ファイルを []byte として読み込む keyfunc を使うと、その偽造された HS256 トークンが検証を通りました。サンプルのように、jwt.WithValidMethods で正確なアルゴリズムを指定し、keyfunc が型付きの鍵を返すようにしてください。

alg: none が検証を通るのは、keyfunc がセンチネル値 jwt.UnsafeAllowNoneSignatureType を返したときだけです。実際の鍵を返すと 'none' signature type is not allowed になります。ES256 は署名時に P-384 の鍵を、詳しい説明のない key is invalid で拒否します。ES256 の署名は JWS が求める 64 バイトの生の r‖s で、DER の署名は crypto/ecdsa: verification error で失敗します。この原因の特定には JWT デバッガーと DER ⇄ 生形式の変換ツールが役立ちます。

v5 ではクレームの検証がデフォルトで有効ですが、exp は任意で(これがないトークンは永久に有効です)、時刻のずれの許容(leeway)もありません。jwt.WithExpirationRequired()、jwt.WithIssuer、jwt.WithAudience、jwt.WithLeeway でこれらの穴をふさげます。エラーは、文字列を比較する代わりに errors.Is(err, jwt.ErrTokenExpired) で判定できます。

ID プロバイダーが発行したトークンを検証するには、JWKS クライアントと組み合わせ、プロバイダーが公開している内容を JWKS チェッカーで確認してください。JWE や JWK の扱いが必要な場合は、go-jose が代わりの選択肢になります。

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

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

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    256/384/512 ビットの HS、RS、PS、ES と EdDSA で署名と検証ができました。ES256K、Ed25519、Ed448 には登録された署名メソッドがありません。

    チェックによる記録
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: no signing method registered
    evidence jws-algorithms: Ed25519: no signing method registered
    evidence jws-algorithms: Ed448: no signing method registered
  • ES256 署名のエンコーディング

    raw

    ES256 の署名は 64 バイトの生の r‖s です。

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

    rejected

    DER エンコードの ES256 署名は token signature is invalid: crypto/ecdsa: verification error で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: token signature is invalid: crypto/ecdsa: verification error
  • アルゴリズムの許可リスト

    from-key

    WithValidMethods を指定しない場合は、鍵の Go の型で決まります。EC 鍵を返す keyfunc に対して RS256 トークンを検証すると、key is of invalid type: RSA verify expects *rsa.PublicKey で失敗しました。

    チェックによる記録
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: RS256 token, keyfunc returning the EC public key, no WithValidMethods: token signature is invalid: key is of invalid type: RSA verify expects *rsa.PublicKey
  • 署名なしトークン(alg none)

    opt-in

    keyfunc が jwt.UnsafeAllowNoneSignatureType を返さない限り、署名のないトークンは 'none' signature type is not allowed で失敗します。

    チェックによる記録
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with the EC key: token signature is invalid: token is unverifiable: 'none' signature type is not allowed
    evidence jwt-alg-none: keyfunc returning jwt.UnsafeAllowNoneSignatureType: <nil>
  • HMAC シークレットとして使われる公開鍵

    rejected

    PEM のテキストを鍵とした HS256 トークンは、*ecdsa.PublicKey を返す keyfunc に対しては失敗しました(HMAC verify expects []byte)が、keyfunc が PEM のバイト列を返すと検証を通りました。

    チェックによる記録
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: keyfunc returning *ecdsa.PublicKey: token signature is invalid: key is of invalid type: HMAC verify expects []byte
    evidence jwt-hmac-public-key: keyfunc returning the PEM bytes (a keyfunc that loads keys as bytes): <nil>
  • alg と曲線の照合

    enforced

    P-384 の鍵で ES256 の署名を行うと key is invalid で失敗します。

    チェックによる記録
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: key is invalid
  • デフォルトのクロックスキュー

    0s

    デフォルトでは時刻のずれを許容しません。3 秒前に期限切れになったトークンは拒否されました。

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

    no

    exp のないトークンは有効でした。WithExpirationRequired() を指定すると token is missing required claim: exp claim is required で失敗します。

    チェックによる記録
    fact jwt-exp-required = no
    evidence jwt-exp-required: with WithExpirationRequired: token has invalid claims: token is missing required claim: exp claim is required

使い方

登録済みクレームに ES256 で署名し、型付きの keyfunc で解析します。署名メソッドを固定し、発行者、有効期限、時刻のずれの許容を適用します。
package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

func main() {
	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	claims := jwt.RegisteredClaims{Subject: "user-42", Issuer: "https://auth.example.com", ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute))}
	signed, _ := jwt.NewWithClaims(jwt.SigningMethodES256, claims).SignedString(key)

	// The keyfunc returns a typed key; the options pin the method and the claims.
	token, err := jwt.ParseWithClaims(signed, &jwt.RegisteredClaims{},
		func(*jwt.Token) (any, error) { return &key.PublicKey, nil },
		jwt.WithValidMethods([]string{"ES256"}),
		jwt.WithIssuer("https://auth.example.com"),
		jwt.WithExpirationRequired(),
		jwt.WithLeeway(30*time.Second),
	)
	fmt.Println("valid:", err == nil && token.Valid, "sub:", token.Claims.(*jwt.RegisteredClaims).Subject)
}

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

コマンド
GOFLAGS=-mod=mod go run .
一致すべき出力(正規表現)
valid: true sub: user-42

ライブラリ