JWT for Go

github.com/malcolmston/jwt

Standard-library-only JSON Web Tokens for Go.

 GitHubports auth0/node-jsonwebtoken

A from-scratch Go implementation of JSON Web Tokens (RFC 7519) on top of the JWS compact serialization (RFC 7515), built entirely on the Go standard library — no third-party modules, no cgo, no require directives. You sign with either <code>NewWithClaims(...).SignedString</code> or the one-shot <code>Sign</code> helper, and verify with <code>Parse</code> / <code>ParseWithClaims</code> driven by a <code>Keyfunc</code> for key selection. Every algorithm — HMAC-SHA (HS256/384/512), RSA PKCS1v15 (RS*), RSA-PSS (PS*), ECDSA (ES*) and an opt-in unsecured none — implements a common SigningMethod interface. RegisteredClaims models the IANA claim set with NumericDate encoding and a string-or-array audience, MapClaims handles arbitrary payloads, and parser options give you method allow-lists, audience/issuer/subject checks, configurable leeway and an injectable clock. Errors are wrapped sentinels you match with errors.Is. The import path is github.com/malcolmston/jwt.

Install

shell
$ go get github.com/malcolmston/jwt

Quick start

main.go
import "github.com/malcolmston/jwt"

claims := jwt.RegisteredClaims{
    Issuer:    "auth.example.com",
    Subject:   "user-42",
    Audience:  jwt.ClaimStrings{"api.example.com"},
    ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
}
signed, _ := jwt.Sign(claims, jwt.SigningMethodHS256, []byte("my-hmac-secret"))

var out jwt.RegisteredClaims
tok, _ := jwt.ParseWithClaims(signed, &out,
    func(*jwt.Token) (any, error) { return []byte("my-hmac-secret"), nil },
    jwt.WithValidMethods([]string{"HS256"}), // reject algorithm confusion
    jwt.WithAudience("api.example.com"),
    jwt.WithIssuer("auth.example.com"))
fmt.Println(tok.Valid, out.Subject)

Features

  • One-call signing — Sign(claims, SigningMethodHS256, []byte(secret)), or build explicitly with NewWithClaims(method, claims).SignedString(key)
  • Verification through Parse (into MapClaims) and ParseWithClaims (into your own Claims), resolving keys via a Keyfunc that sees the header for kid selection
  • Every algorithm behind one SigningMethod interface — HMAC SigningMethodHS256/384/512, RSA RS*, RSA-PSS PS*, ECDSA ES* (fixed-width r||s), plus opt-in none
  • RegisteredClaims covers iss, sub, aud, exp, nbf, iat, jti with NumericDate epoch-seconds and string-or-array ClaimStrings audience
  • MapClaims for arbitrary payloads, or any custom struct satisfying the one-method Claims interface (embed RegisteredClaims)
  • Parser options — WithValidMethods (defeat alg-confusion), WithAudience, WithIssuer, WithSubject, WithLeeway, WithExpirationRequired, WithIssuedAt
  • Deterministic time — WithClock / WithTimeFunc and the ClockFunc adapter make exp/nbf/iat validation reproducible in tests
  • PEM key helpers — ParseRSAPrivateKeyFromPEM, ParseRSAPublicKeyFromPEM, ParseECPrivateKeyFromPEM, ParseECPublicKeyFromPEM (PKCS#1/SEC1/PKCS#8/PKIX)
  • Wrapped sentinel errors — ErrTokenExpired, ErrSignatureInvalid, ErrTokenInvalidAudience, ErrTokenNotValidYet and more, all matchable with errors.Is
  • Double opt-in none — the parser needs WithAllowNone and the UnsafeAllowNoneSignatureType sentinel key, so unsecured tokens never slip through by default
  • Base64url (no padding) header/payload/signature encoding, tag headers with SetKID, expose the signing input via SigningString
  • Zero dependencies — pure Go standard library (crypto/*, encoding/*, math/big), no cgo, nothing to audit but the toolchain
JWT