JWT for Go
Standard-library-only JSON Web Tokens for Go.
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
$ go get github.com/malcolmston/jwtQuick start
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 withNewWithClaims(method, claims).SignedString(key) - Verification through
Parse(intoMapClaims) andParseWithClaims(into your ownClaims), resolving keys via aKeyfuncthat sees the header forkidselection - Every algorithm behind one
SigningMethodinterface — HMACSigningMethodHS256/384/512, RSARS*, RSA-PSSPS*, ECDSAES*(fixed-width r||s), plus opt-innone RegisteredClaimscovers iss, sub, aud, exp, nbf, iat, jti withNumericDateepoch-seconds and string-or-arrayClaimStringsaudienceMapClaimsfor arbitrary payloads, or any custom struct satisfying the one-methodClaimsinterface (embedRegisteredClaims)- Parser options —
WithValidMethods(defeat alg-confusion),WithAudience,WithIssuer,WithSubject,WithLeeway,WithExpirationRequired,WithIssuedAt - Deterministic time —
WithClock/WithTimeFuncand theClockFuncadapter 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,ErrTokenNotValidYetand more, all matchable witherrors.Is - Double opt-in
none— the parser needsWithAllowNoneand theUnsafeAllowNoneSignatureTypesentinel key, so unsecured tokens never slip through by default - Base64url (no padding) header/payload/signature encoding, tag headers with
SetKID, expose the signing input viaSigningString - Zero dependencies — pure Go standard library (crypto/*, encoding/*, math/big), no cgo, nothing to audit but the toolchain