Перейти к содержимому
ecdsa.com
Go

golang-jwt/jwt

golang-jwt/jwt — поддерживаемый сообществом преемник dgrijalva/jwt-go и самый используемый пакет JWT для Go: проверка, привязанная к типу ключа, 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 ключами из стандартной библиотеки: []byte для HMAC, *rsa.PublicKey, *ecdsa.PublicKey и ed25519.PublicKey. В версии 5.3.1 подпись и проверка работали для семейств HS, RS, PS и ES, а также для EdDSA; для ES256K и полностью определённых имён Ed25519 и Ed448 зарегистрированного метода подписи нет.

Проверка целиком зависит от функции keyfunc, которую вы передаёте в jwt.Parse. Каждый метод подписи проверяет тип Go полученного ключа, поэтому с keyfunc, возвращающей *ecdsa.PublicKey, токен RS256 отклонён с ошибкой RSA verify expects *rsa.PublicKey, а токен HS256, подписанный текстом PEM в качестве ключа, — с ошибкой HMAC verify expects []byte. Защита исчезает, если keyfunc возвращает сырые байты для любого токена: с keyfunc, которая загружает PEM-файл как []byte, этот поддельный токен HS256 в ходе проверки был принят. Передавайте jwt.WithValidMethods с конкретным алгоритмом и оставляйте keyfunc типизированной, как в примере.

Токен с alg: none проходит проверку, только если keyfunc возвращает сигнальное значение jwt.UnsafeAllowNoneSignatureType; с любым настоящим ключом возникает ошибка 'none' signature type is not allowed. ES256 отказывается подписывать ключом P-384 и выдаёт лишь краткое key is invalid. Подписи ES256 — это 64-байтовый формат raw r‖s, которого требует JWS; подпись DER не проходит проверку с ошибкой crypto/ecdsa: verification error, а найти причину помогут отладчик JWT и конвертер DER ⇄ raw.

В v5 проверка утверждений включена по умолчанию, но exp необязателен (токен без него действителен вечно), а допуска на расхождение часов нет. Эти пробелы закрывают jwt.WithExpirationRequired(), jwt.WithIssuer, jwt.WithAudience и jwt.WithLeeway; ошибки можно сопоставлять через errors.Is(err, jwt.ErrTokenExpired), а не сравнивать строки.

Чтобы проверять токены от поставщика удостоверений, используйте пакет вместе с клиентом JWKS, а что именно публикует поставщик, покажет проверка JWKS; если нужна работа с JWE или JWK, альтернатива — go-jose.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/golang-jwt/run.sh запустила golang-jwt/jwt 5.3.1 в образе контейнера golang:1.27.3-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Подпись и проверка работают для HS, RS, PS и ES на 256/384/512 бит и для 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 байта в формате raw r‖s.

    Что записала проверка
    fact jws-ecdsa-encoding = raw
  • Подпись в DER внутри JWS

    rejected

    Подпись ES256 в кодировке DER не проходит проверку с ошибкой 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: токен RS256 при keyfunc, возвращающей ключ EC, отклонён с ошибкой 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

    Неподписанный токен отклоняется с ошибкой 'none' signature type is not allowed, если keyfunc не возвращает jwt.UnsafeAllowNoneSignatureType.

    Что записала проверка
    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

    Токен HS256, подписанный текстом PEM в качестве ключа, не прошёл проверку с keyfunc, возвращающей *ecdsa.PublicKey (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

    При подписи ES256 ключом P-384 возникает ошибка 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

Библиотеки