Documentation
ΒΆ
Overview ΒΆ
Package zerojwt provides an ultra-fast, zero-allocation (0 B/op, 0 allocs/op) JWT validator and signer in pure Go supporting HS256, HS384, HS512, EdDSA (Ed25519), and ES256.
Deprecated: Use github.com/KaikeGM/zerojwt/v2 instead.
Index ΒΆ
- Constants
- Variables
- func AppendEscapedJSONString(dst, s []byte) []byte
- func ArrayElements(rawArray []byte, errOut ...*error) iter.Seq2[[]byte, bool]
- func NextArrayElement(b []byte) (val []byte, isStr bool, rest []byte, ok bool)
- func NextJSONProp(b []byte) (key, val []byte, isStr bool, rest []byte, ok bool)
- func ObjectProperties(rawObject []byte, errOut ...*error) iter.Seq[JSONProp]
- func ObjectProps(rawObject []byte, errOut ...*error) iter.Seq2[[]byte, []byte]
- func ParseNumeric(b []byte) (float64, bool)
- func ParsePositiveInt(val []byte) (int64, bool)
- func TrimJSONArray(b []byte) ([]byte, bool)
- func TrimJSONObject(b []byte) ([]byte, bool)
- func UnescapeJSONInPlace(s []byte) []byte
- type Algorithm
- type Claims
- type ClaimsLarge
- type JSONProp
- type Key
- type Options
- type Scratch
- type SignBuilder
- func (b *SignBuilder) Discard()
- func (b *SignBuilder) SetAudience(aud []byte) *SignBuilder
- func (b *SignBuilder) SetClaimBool(key string, val bool) *SignBuilder
- func (b *SignBuilder) SetClaimBytes(key string, val []byte) *SignBuilder
- func (b *SignBuilder) SetClaimInt(key string, val int64) *SignBuilder
- func (b *SignBuilder) SetClaimRawJSON(key string, rawJSON []byte) *SignBuilder
- func (b *SignBuilder) SetClaimString(key, val string) *SignBuilder
- func (b *SignBuilder) SetExpiresAt(exp int64) *SignBuilder
- func (b *SignBuilder) SetIssuedAt(iat int64) *SignBuilder
- func (b *SignBuilder) SetIssuer(iss []byte) *SignBuilder
- func (b *SignBuilder) SetJTI(jti []byte) *SignBuilder
- func (b *SignBuilder) SetNotBefore(nbf int64) *SignBuilder
- func (b *SignBuilder) SetSubject(sub []byte) *SignBuilder
- func (b *SignBuilder) SetUsername(username []byte) *SignBuilder
- func (b *SignBuilder) Sign() ([]byte, error)
- type Verifier
- func NewES256Verifier(pub *ecdsa.PublicKey, priv *ecdsa.PrivateKey, opts Options) *Verifier
- func NewEd25519Verifier(pub ed25519.PublicKey, priv ed25519.PrivateKey, opts Options) *Verifier
- func NewSecureVerifier(secret []byte) (*Verifier, error)
- func NewSecureVerifierWithOptions(secret []byte, opts Options) (*Verifier, error)
- func NewVerifier(secret []byte) *Verifier
- func NewVerifierWithOptions(secret []byte, opts Options) *Verifier
- func TryNewVerifier(secret []byte, opts Options) (*Verifier, error)
- func (v *Verifier) CanSign() bool
- func (v *Verifier) Destroy()
- func (v *Verifier) Middleware(next http.Handler) http.Handler
- func (v *Verifier) NewScratch() *Scratch
- func (v *Verifier) NewSigner(dst []byte) SignBuilder
- func (v *Verifier) NewSignerWithScratch(dst []byte, st *Scratch) SignBuilder
- func (v *Verifier) ProtectFunc(next func(w http.ResponseWriter, r *http.Request, claims *Claims)) http.HandlerFunc
- func (v *Verifier) Sign(dst []byte, payloadJSON []byte) []byte
- func (v *Verifier) SignClaims(dst []byte, sub, username []byte, iat, exp int64) []byte
- func (v *Verifier) SignHS256(dst []byte, payloadJSON []byte) []byte
- func (v *Verifier) SignHS256WithScratch(dst []byte, payloadJSON []byte, st *Scratch) []byte
- func (v *Verifier) SignWithScratch(dst []byte, payloadJSON []byte, st *Scratch) []byte
- func (v *Verifier) TrySign(dst []byte, payloadJSON []byte) ([]byte, error)
- func (v *Verifier) TrySignClaims(dst []byte, sub, username []byte, iat, exp int64) ([]byte, error)
- func (v *Verifier) TrySignClaimsWithScratch(dst []byte, sub, username []byte, iat, exp int64, st *Scratch) ([]byte, error)
- func (v *Verifier) TrySignWithScratch(dst []byte, payloadJSON []byte, st *Scratch) ([]byte, error)
- func (v *Verifier) Verify(tok []byte, nowUnix int64, out *Claims) error
- func (v *Verifier) VerifyBearer(authHeader []byte, nowUnix int64, out *Claims) error
- func (v *Verifier) VerifyCustom(tok []byte, nowUnix int64, onClaim func(key, val []byte, isStr bool) bool) error
- func (v *Verifier) VerifyLarge(tok []byte, nowUnix int64, out *ClaimsLarge) error
- func (v *Verifier) VerifyLargeWithScratch(tok []byte, nowUnix int64, out *ClaimsLarge, st *Scratch) error
- func (v *Verifier) VerifyWithScratch(tok []byte, nowUnix int64, out *Claims, st *Scratch) error
Examples ΒΆ
Constants ΒΆ
const ( // MaxHeaderSize is the default maximum decoded byte length of a JWT header in zero-alloc mode. MaxHeaderSize = 128 // MaxPayloadSize is the default maximum decoded byte length of a JWT payload in zero-alloc mode. MaxPayloadSize = 1024 )
Variables ΒΆ
var ( // ErrMissingBearer is returned by VerifyBearer when the "Bearer " prefix is absent. ErrMissingBearer = errors.New("zerojwt: missing Bearer prefix") // ErrInvalidToken is returned when the token does not have 3 valid Base64URL segments. ErrInvalidToken = errors.New("zerojwt: malformed token structure") // ErrInvalidHeader is returned when the JWT header is not valid JSON or specifies an unsupported algorithm. ErrInvalidHeader = errors.New("zerojwt: invalid header or unsupported algorithm") // ErrInvalidSignature is returned when the cryptographic signature verification fails. ErrInvalidSignature = errors.New("zerojwt: signature verification failed") // ErrExpiredToken is returned when the current timestamp is >= the "exp" claim. ErrExpiredToken = errors.New("zerojwt: token is expired") // ErrNotValidYet is returned when the current timestamp is < the "nbf" claim. ErrNotValidYet = errors.New("zerojwt: token is not valid yet (nbf)") // ErrInvalidPayload is returned when the payload JSON is malformed or required claims are invalid. ErrInvalidPayload = errors.New("zerojwt: invalid token payload claims") // ErrPayloadTooLarge is returned when the decoded payload exceeds the configured MaxPayloadSize. ErrPayloadTooLarge = errors.New("zerojwt: payload exceeds maximum zero-alloc buffer size") // ErrInvalidIssuer is returned when the "iss" claim is missing or does not match ExpectedIssuer. ErrInvalidIssuer = errors.New("zerojwt: invalid or mismatched token issuer (iss)") // ErrInvalidAudience is returned when the "aud" claim is missing or does not match ExpectedAudience. ErrInvalidAudience = errors.New("zerojwt: invalid or mismatched token audience (aud)") // ErrUnknownKeyID is returned when the header specifies a "kid" that is not registered in the Verifier. ErrUnknownKeyID = errors.New("zerojwt: unknown key ID (kid) in header") // ErrMissingExp is returned when Options.RequireExp is set and the token has no "exp" claim. ErrMissingExp = errors.New("zerojwt: token has no exp claim") // ErrInvalidConfig is wrapped by the errors TryNewVerifier returns (and the panics of the // New* constructors) for unusable configurations such as an empty HMAC secret. ErrInvalidConfig = errors.New("zerojwt: invalid configuration") // ErrNoSigningKey is returned by SignBuilder.Sign when the Verifier cannot sign (see Verifier.CanSign). ErrNoSigningKey = errors.New("zerojwt: verifier has no signing key") // ErrVerifierDestroyed is returned by Verify* after Verifier.Destroy has been called. ErrVerifierDestroyed = errors.New("zerojwt: verifier has been destroyed") // ErrSigningFailed is returned by TrySign* when cryptographic signing fails // (such as an invalid private key scalar or signing primitive failure). ErrSigningFailed = errors.New("zerojwt: cryptographic signing failed") )
Pre-allocated static errors returned by zerojwt. Comparing against these errors with errors.Is or == performs zero allocations.
Functions ΒΆ
func AppendEscapedJSONString ΒΆ
AppendEscapedJSONString appends a JSON-escaped string (with surrounding quotes) to dst using unrolled 8-byte vectorized span checks and bulk memory copies.
func ArrayElements ΒΆ added in v1.2.0
ArrayElements returns an iter.Seq2[[]byte, bool] (element, isString) that traverses a raw JSON array (e.g., `["admin", "user"]`) in-place with guaranteed 0 B/op and 0 allocs/op. String elements are yielded as slices into the underlying buffer (without outer quotes); they remain in their raw escaped form to preserve buffer immutability across multiple passes. Call UnescapeJSONInPlace(elem) if unescaped content is needed. An optional errOut pointer can be passed to capture any JSON syntax error or truncation; if omitted or if nil, iteration terminates without assigning an error.
func NextArrayElement ΒΆ
NextArrayElement extracts the next element from a JSON array body (inside '[' and ']'). It operates strictly in-place on b with zero heap allocations (0 B/op, 0 allocs/op). String elements have their enclosing quotes stripped and are returned as non-destructive slices into b, preserving any escape sequences so that b is not modified and can be traversed multiple times safely. Use UnescapeJSONInPlace(val) if unescaped content is needed.
func NextJSONProp ΒΆ
NextJSONProp extracts the next top-level "key": value pair from a JSON object body (inside '{' and '}'). It operates strictly in-place on b with zero heap allocations (0 B/op, 0 allocs/op). String values have their enclosing quotes stripped and are returned as non-destructive slices into b, preserving any escape sequences (e.g. \" or \u00e7) so that b is not modified and can be traversed multiple times. Use UnescapeJSONInPlace(val) if unescaped content is needed.
func ObjectProperties ΒΆ added in v1.2.0
ObjectProperties returns an iter.Seq[JSONProp] that traverses a raw JSON object in-place, yielding key, value, and isString type information with 0 B/op and 0 allocs/op. An optional errOut pointer can be passed to capture any JSON syntax error or truncation.
func ObjectProps ΒΆ added in v1.2.0
ObjectProps returns an iter.Seq2[[]byte, []byte] (key, val) that traverses a raw JSON object (e.g., `{"role": "admin", "tenant": "acme"}`) in-place with guaranteed 0 B/op and 0 allocs/op. Values remain in their raw escaped form to preserve buffer immutability across multiple passes. Call UnescapeJSONInPlace(val) if unescaped content is needed. An optional errOut pointer can be passed to capture any JSON syntax error or truncation.
func ParseNumeric ΒΆ
ParseNumeric parses a JSON number (integer or floating point, e.g. Unix timestamps) without allocations.
func ParsePositiveInt ΒΆ
ParsePositiveInt parses a strictly positive 64-bit integer from a byte slice of digits. Supports full int64 range up to 19 digits (e.g. distributed snowflake IDs).
func TrimJSONArray ΒΆ
TrimJSONArray strips surrounding whitespace and outer '[' and ']' brackets from a JSON array.
func TrimJSONObject ΒΆ
TrimJSONObject strips surrounding whitespace and outer '{' and '}' braces from a JSON object.
func UnescapeJSONInPlace ΒΆ
UnescapeJSONInPlace unescapes JSON string escape sequences in-place without allocating memory.
Types ΒΆ
type Algorithm ΒΆ
type Algorithm string
Algorithm represents a supported JWS cryptographic algorithm (RFC 7518 / RFC 8037).
type Claims ΒΆ
type Claims struct {
SubjectID int64 // Parsed positive integer ID when "sub" is numeric (e.g. "123" -> 123)
IssuedAt int64 // "iat" claim (Unix seconds)
ExpiresAt int64 // "exp" claim (Unix seconds)
NotBefore int64 // "nbf" claim (Unix seconds)
HasSub bool // True if "sub" claim was present
HasUsername bool // True if "username" claim was present
HasIss bool // True if "iss" claim was present
HasAud bool // True if "aud" claim was present
HasJti bool // True if "jti" claim was present
HasExp bool // True if "exp" claim was present
HasNbf bool // True if "nbf" claim was present
HasIat bool // True if "iat" claim was present
// contains filtered or unexported fields
}
Claims holds standard JWT claims on the caller's stack without heap allocations. Its internal fixed buffers own the copied claim bytes, ensuring 100% safety against data races even after the internal Scratch buffer returns to the pool.
func ClaimsFromContext ΒΆ
ClaimsFromContext retrieves the verified *Claims from a request context populated by Verifier.Middleware.
func (*Claims) Audience ΒΆ
Audience returns the primary "aud" claim as a byte slice backed by Claims' inline buffer.
func (*Claims) Clear ΒΆ
func (c *Claims) Clear()
Clear resets all fields and explicitly zeroes all internal claim buffers with 0x00 (memclr).
func (*Claims) Issuer ΒΆ
Issuer returns the "iss" claim as a byte slice backed by Claims' inline buffer.
func (*Claims) JTI ΒΆ
JTI returns the "jti" (JWT ID) claim as a byte slice backed by Claims' inline buffer.
func (*Claims) Reset ΒΆ
func (c *Claims) Reset()
Reset resets length markers and flags for fast reuse without zeroing the underlying byte arrays.
type ClaimsLarge ΒΆ
type ClaimsLarge struct {
SubjectID int64
IssuedAt int64
ExpiresAt int64
NotBefore int64
HasSub bool
HasUsername bool
HasIss bool
HasAud bool
HasJti bool
HasExp bool
HasNbf bool
HasIat bool
// contains filtered or unexported fields
}
ClaimsLarge provides expanded 256-byte inline buffers for deployments where Subject, Username, Issuer, Audience, or JTI identifiers exceed 64/128 bytes.
func (*ClaimsLarge) Audience ΒΆ
func (c *ClaimsLarge) Audience() []byte
func (*ClaimsLarge) Clear ΒΆ
func (c *ClaimsLarge) Clear()
func (*ClaimsLarge) Issuer ΒΆ
func (c *ClaimsLarge) Issuer() []byte
func (*ClaimsLarge) JTI ΒΆ
func (c *ClaimsLarge) JTI() []byte
func (*ClaimsLarge) Reset ΒΆ
func (c *ClaimsLarge) Reset()
func (*ClaimsLarge) Subject ΒΆ
func (c *ClaimsLarge) Subject() []byte
func (*ClaimsLarge) Username ΒΆ
func (c *ClaimsLarge) Username() []byte
type JSONProp ΒΆ added in v1.2.0
JSONProp represents a key-value property scanned in-place from a JSON object.
type Key ΒΆ
type Key struct {
ID string
Secret []byte
Ed25519Public ed25519.PublicKey
ECDSAPublic *ecdsa.PublicKey
}
Key represents a named cryptographic key for "kid" (Key ID) rotation in JWT headers.
type Options ΒΆ
type Options struct {
// Algorithm selects the JWS algorithm (default: AlgHS256).
Algorithm Algorithm
// MaxHeaderSize sets the maximum decoded byte length of a JWT header (default: 128).
MaxHeaderSize int
// MaxPayloadSize sets the maximum decoded byte length of a JWT payload (default: 1024).
MaxPayloadSize int
// Leeway defines the clock skew tolerance for "exp" and "nbf" validation.
Leeway time.Duration
// ExpectedIssuer enforces that the token's "iss" claim matches this value.
ExpectedIssuer string
// ExpectedAudience enforces that the token's "aud" claim (string or array) contains this value.
ExpectedAudience string
// Keys registers named keys for automatic "kid" header lookup during verification.
Keys []Key
// ActiveKeyID embeds `"kid":"<ActiveKeyID>"` in the JWT header when signing tokens.
ActiveKeyID string
// Ed25519PublicKey is the 32-byte public key used when Algorithm is AlgEdDSA.
Ed25519PublicKey ed25519.PublicKey
// Ed25519PrivateKey is the 64-byte private key used for signing when Algorithm is AlgEdDSA.
Ed25519PrivateKey ed25519.PrivateKey
// ECDSAPublicKey is the P-256 public key used when Algorithm is AlgES256.
ECDSAPublicKey *ecdsa.PublicKey
// ECDSAPrivateKey is the P-256 private key used for signing when Algorithm is AlgES256.
ECDSAPrivateKey *ecdsa.PrivateKey
// ZeroMemory zeroes out sensitive scratch buffers with 0x00 (memclr) after each operation.
// This is best-effort memory hygiene, not a compliance guarantee.
ZeroMemory bool
// RequireExp makes verification fail with ErrMissingExp when a token carries no "exp"
// claim, and makes SignClaims refuse to sign with exp <= 0. Default: false.
RequireExp bool
// StrictSecretLength enforces the RFC 7518 Β§3.2 minimum HMAC key size
// (32 bytes for HS256, 48 for HS384, 64 for HS512). Empty keys are always rejected,
// regardless of this option. Default: false.
StrictSecretLength bool
}
Options configures algorithm, buffer dimensions, RFC 7519 validation rules, key rotation ("kid"), and FIPS/PCI-DSS memory sanitization for a Verifier.
func SecureOptions ΒΆ added in v1.3.0
func SecureOptions() Options
SecureOptions returns Options preconfigured with strict production security settings aligning with OIDC Core 1.0 Β§3.1.3.7, OAuth 2.0 RFC 9068 Β§2.2, and RFC 7518 Β§3.2:
- RequireExp: true (rejects tokens without "exp" claim)
- StrictSecretLength: true (enforces minimum HMAC key sizes: 32B for HS256, 48B for HS384, 64B for HS512)
Note: StrictSecretLength applies to symmetric HMAC algorithms; asymmetric algorithms (EdDSA, ES256) have curve-mandated key sizes enforced directly by mathematical checks.
type Scratch ΒΆ
type Scratch struct {
// contains filtered or unexported fields
}
Scratch holds reusable buffers and pre-initialized HMAC hashers. In multi-goroutine servers, Verifier manages a sync.Pool of Scratch instances automatically. In single-threaded event loops (such as gnet with LockOSThread), callers can hold a single Scratch instance and call VerifyWithScratch to bypass even sync.Pool overhead.
type SignBuilder ΒΆ
type SignBuilder struct {
// contains filtered or unexported fields
}
SignBuilder is a stack-allocated, zero-allocation fluent builder for constructing and signing JWT tokens with arbitrary custom claims.
func (*SignBuilder) Discard ΒΆ added in v1.2.0
func (b *SignBuilder) Discard()
Discard releases the internal Scratch buffer back to the pool if owned, invalidating the builder. It is safe to call Discard multiple times or after Sign. Typical usage:
b := v.NewSigner(dst) defer b.Discard()
func (*SignBuilder) SetAudience ΒΆ
func (b *SignBuilder) SetAudience(aud []byte) *SignBuilder
SetAudience appends the standard "aud" claim.
func (*SignBuilder) SetClaimBool ΒΆ
func (b *SignBuilder) SetClaimBool(key string, val bool) *SignBuilder
SetClaimBool appends a custom boolean claim.
func (*SignBuilder) SetClaimBytes ΒΆ
func (b *SignBuilder) SetClaimBytes(key string, val []byte) *SignBuilder
SetClaimBytes appends a custom string claim from a byte slice with JSON escaping.
func (*SignBuilder) SetClaimInt ΒΆ
func (b *SignBuilder) SetClaimInt(key string, val int64) *SignBuilder
SetClaimInt appends a custom 64-bit integer claim.
func (*SignBuilder) SetClaimRawJSON ΒΆ
func (b *SignBuilder) SetClaimRawJSON(key string, rawJSON []byte) *SignBuilder
SetClaimRawJSON appends a pre-formatted JSON value (such as an array `["admin","editor"]` or object). The value is copied verbatim and NOT validated or escaped: never pass untrusted input, or the resulting payload may be malformed or contain injected claims.
func (*SignBuilder) SetClaimString ΒΆ
func (b *SignBuilder) SetClaimString(key, val string) *SignBuilder
SetClaimString appends a custom string claim with JSON escaping.
func (*SignBuilder) SetExpiresAt ΒΆ
func (b *SignBuilder) SetExpiresAt(exp int64) *SignBuilder
SetExpiresAt appends the standard "exp" claim (Unix seconds).
func (*SignBuilder) SetIssuedAt ΒΆ
func (b *SignBuilder) SetIssuedAt(iat int64) *SignBuilder
SetIssuedAt appends the standard "iat" claim (Unix seconds).
func (*SignBuilder) SetIssuer ΒΆ
func (b *SignBuilder) SetIssuer(iss []byte) *SignBuilder
SetIssuer appends the standard "iss" claim.
func (*SignBuilder) SetJTI ΒΆ
func (b *SignBuilder) SetJTI(jti []byte) *SignBuilder
SetJTI appends the standard "jti" (JWT ID) claim.
func (*SignBuilder) SetNotBefore ΒΆ
func (b *SignBuilder) SetNotBefore(nbf int64) *SignBuilder
SetNotBefore appends the standard "nbf" claim (Unix seconds).
func (*SignBuilder) SetSubject ΒΆ
func (b *SignBuilder) SetSubject(sub []byte) *SignBuilder
SetSubject appends the standard "sub" claim.
func (*SignBuilder) SetUsername ΒΆ
func (b *SignBuilder) SetUsername(username []byte) *SignBuilder
SetUsername appends the "username" claim.
func (*SignBuilder) Sign ΒΆ
func (b *SignBuilder) Sign() ([]byte, error)
Sign finalizes the JSON payload, signs the token into dst, and returns the resulting slice. If the SignBuilder was created via NewSigner, the internal Scratch is returned to the pool.
type Verifier ΒΆ
type Verifier struct {
// contains filtered or unexported fields
}
Verifier validates and signs JWT tokens with zero heap allocations (0 B/op, 0 allocs/op). A Verifier is 100% safe for concurrent use by multiple goroutines.
func NewES256Verifier ΒΆ
NewES256Verifier creates a new concurrent-safe ECDSA P-256 (ES256) Verifier. For verification-only nodes, priv can be nil.
func NewEd25519Verifier ΒΆ
NewEd25519Verifier creates a new concurrent-safe EdDSA (Ed25519) Verifier. For verification-only nodes, priv can be nil.
Example ΒΆ
ExampleNewEd25519Verifier demonstrates an asymmetric microservices architecture where the authentication authority signs with an Ed25519 private key, and consuming microservices verify tokens using only the 32-byte public key (privKey = nil).
package main
import (
"crypto/ed25519"
"fmt"
"github.com/KaikeGM/zerojwt"
)
func main() {
// In production, keys are loaded from secure config/vault.
// We generate an Ed25519 keypair for demonstration:
pubKey, privKey, err := ed25519.GenerateKey(nil)
if err != nil {
fmt.Printf("Key generation error: %v\n", err)
return
}
now := int64(1700000000)
// 1. Authentication Server: initialized with both pubKey and privKey to sign tokens.
authServer := zerojwt.NewEd25519Verifier(pubKey, privKey, zerojwt.Options{
ExpectedIssuer: "auth.internal",
})
var tokBuf [512]byte
token := authServer.SignClaims(tokBuf[:0], []byte("srv_99"), []byte("payment-worker"), now, now+1800)
// 2. Client Microservice: initialized with public key ONLY (privKey is nil).
// This ensures the microservice can verify tokens, but cannot forge or issue them.
microserviceVerifier := zerojwt.NewEd25519Verifier(pubKey, nil, zerojwt.Options{
ExpectedIssuer: "auth.internal",
})
var claims zerojwt.Claims
if err := microserviceVerifier.Verify(token, now, &claims); err != nil {
fmt.Printf("Microservice verification failed: %v\n", err)
return
}
fmt.Printf("Microservice verified token for %s (issuer valid: %t)\n", claims.Username(), claims.HasIss)
}
Output: Microservice verified token for payment-worker (issuer valid: true)
func NewSecureVerifier ΒΆ added in v1.3.0
NewSecureVerifier creates a new concurrent-safe Verifier preconfigured for production profiles (OIDC Core 1.0 Β§3.1.3.7, RFC 9068 Β§2.2, RFC 7518 Β§3.2). It enforces RequireExp: true and StrictSecretLength: true (minimum 32 bytes for HS256). Returns an error wrapping ErrInvalidConfig if the secret is too short or empty.
func NewSecureVerifierWithOptions ΒΆ added in v1.3.0
NewSecureVerifierWithOptions creates a new concurrent-safe Verifier applying RequireExp and StrictSecretLength over the caller-provided Options.
func NewVerifier ΒΆ
NewVerifier creates a new concurrent-safe HS256 Verifier for the given secret key using the default buffer limits (128B header, 1024B payload).
Note: NewVerifier preserves RFC 7519 baseline defaults (RequireExp: false, StrictSecretLength: false). For production environments, prefer NewSecureVerifier.
func NewVerifierWithOptions ΒΆ
NewVerifierWithOptions creates a new concurrent-safe Verifier with custom algorithm, buffer dimensions, clock skew leeway, issuer/audience rules, and key rotation.
It panics with an error wrapping ErrInvalidConfig when the configuration is unusable (empty HMAC secret, a Key without material for the selected algorithm, unknown algorithm, wrong key size or curve, ...), so that misconfiguration is caught at startup instead of producing a verifier that accepts forged tokens. Use TryNewVerifier to get an error instead.
func TryNewVerifier ΒΆ added in v1.1.0
TryNewVerifier is like NewVerifierWithOptions but returns an error wrapping ErrInvalidConfig instead of panicking. opts.Algorithm selects the algorithm; for EdDSA and ES256 the keys come from the corresponding Options fields and secret is ignored.
func (*Verifier) CanSign ΒΆ added in v1.1.0
CanSign reports whether this Verifier holds the key material needed to issue tokens. Verify-only nodes (for example EdDSA or ES256 without a private key) return false, and Sign* methods return nil on them instead of producing a token with an invalid signature.
func (*Verifier) Destroy ΒΆ
func (v *Verifier) Destroy()
Destroy makes the Verifier unusable and wipes, on a best-effort basis, the key material it owns: the HMAC secret, its private copy of the Ed25519 private key and the secrets of registered Keys. Afterwards Verify* returns ErrVerifierDestroyed and Sign* returns nil.
This is memory hygiene, not a compliance guarantee. Not scrubbed: the slices you passed in, HMAC state cached inside pooled Scratch values (the standard library's hmac keeps key-derived pads) and an *ecdsa.PrivateKey you share with the Verifier (only the reference is dropped). Destroy must not be called concurrently with other methods.
func (*Verifier) Middleware ΒΆ
Middleware provides a standard net/http and chi compatible middleware that validates the Authorization: Bearer <token> header and attaches *Claims to the request's context.Context.
func (*Verifier) NewScratch ΒΆ
NewScratch allocates a new Scratch state bound to this Verifier's keys and buffer sizes. Useful for single-threaded event-loop servers (e.g. gnet) using VerifyWithScratch.
func (*Verifier) NewSigner ΒΆ
func (v *Verifier) NewSigner(dst []byte) SignBuilder
NewSigner creates a fluent SignBuilder backed by the Verifier's Scratch pool. Calling Sign() automatically releases the internal Scratch back to the pool.
Example ΒΆ
ExampleVerifier_NewSigner demonstrates using the fluent SignBuilder to compose and sign a JWT with custom claims entirely on the stack with 0 heap allocations.
package main
import (
"fmt"
"github.com/KaikeGM/zerojwt"
)
func main() {
secret := []byte("super-secret-key-32-bytes-long!!")
verifier := zerojwt.NewVerifier(secret)
now := int64(1700000000)
var tokBuf [512]byte
b := verifier.NewSigner(tokBuf[:0])
b.SetSubject([]byte("user_456")).
SetUsername([]byte("bob")).
SetIssuer([]byte("auth.service")).
SetAudience([]byte("api.service")).
SetClaimString("role", "admin").
SetClaimInt("tenant_id", 42).
SetClaimBool("mfa", true).
SetExpiresAt(now + 3600)
token, err := b.Sign()
if err != nil {
fmt.Printf("Sign error: %v\n", err)
return
}
var claims zerojwt.Claims
if err := verifier.Verify(token, now, &claims); err != nil {
fmt.Printf("Verify error: %v\n", err)
return
}
fmt.Printf("Subject: %s\n", claims.Subject())
fmt.Printf("Username: %s\n", claims.Username())
fmt.Printf("HasExp: %t\n", claims.HasExp)
}
Output: Subject: user_456 Username: bob HasExp: true
func (*Verifier) NewSignerWithScratch ΒΆ
func (v *Verifier) NewSignerWithScratch(dst []byte, st *Scratch) SignBuilder
NewSignerWithScratch creates a fluent SignBuilder using a caller-supplied Scratch (ideal for single-threaded event loops like gnet).
func (*Verifier) ProtectFunc ΒΆ
func (v *Verifier) ProtectFunc(next func(w http.ResponseWriter, r *http.Request, claims *Claims)) http.HandlerFunc
ProtectFunc wraps a handler that receives stack-allocated *Claims directly, achieving 0 B/op and 0 allocs/op per authenticated HTTP request.
Example ΒΆ
ExampleVerifier_ProtectFunc demonstrates wrapping an HTTP handler with ultra-low latency and 0 heap allocations per authenticated request.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/KaikeGM/zerojwt"
)
func main() {
secret := []byte("super-secret-key-32-bytes-long!!")
verifier := zerojwt.NewVerifier(secret)
now := time.Now().Unix()
// Create an authenticated HTTP handler receiving stack-allocated *Claims
handler := verifier.ProtectFunc(func(w http.ResponseWriter, r *http.Request, claims *zerojwt.Claims) {
fmt.Fprintf(w, "Hello, %s!", claims.Username())
})
// Sign a test token
var tokBuf [512]byte
token := verifier.SignClaims(tokBuf[:0], []byte("u1"), []byte("charlie"), now, now+3600)
// Simulate an HTTP request with the Bearer token
req := httptest.NewRequest("GET", "/profile", nil)
req.Header.Set("Authorization", "Bearer "+string(token))
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
fmt.Printf("Status: %d, Body: %s\n", rec.Code, rec.Body.String())
}
Output: Status: 200, Body: Hello, charlie!
func (*Verifier) Sign ΒΆ
Sign signs a raw JSON payload with the Verifier's configured algorithm (HS256, HS384, HS512, EdDSA, ES256) and appends the complete JWT token ("header.payload.signature") to dst with zero heap allocations (when dst has sufficient capacity).
Sign reports failure by returning nil: see TrySign for typed errors.
func (*Verifier) SignClaims ΒΆ
SignClaims formats a standard JSON payload `{"sub":"...","username":"...","iat":...,"exp":...}` into Scratch and appends the signed JWT token to dst with zero heap allocations.
It returns nil on any failure condition (see TrySignClaims for typed errors).
func (*Verifier) SignHS256 ΒΆ
SignHS256 signs a raw JSON payload and appends the complete JWT token ("header.payload.signature") to dst with zero heap allocations (when dst has sufficient capacity). It reports failure by returning nil: see TrySign for typed errors.
func (*Verifier) SignHS256WithScratch ΒΆ
SignHS256WithScratch signs a raw JSON payload using a caller-supplied Scratch state. It reports failure by returning nil: see TrySignWithScratch for typed errors.
func (*Verifier) SignWithScratch ΒΆ
SignWithScratch signs a raw JSON payload using a caller-supplied Scratch state. It reports failure by returning nil: see TrySignWithScratch for typed errors.
func (*Verifier) TrySign ΒΆ added in v1.3.0
TrySign signs a raw JSON payload with the Verifier's configured algorithm (HS256, HS384, HS512, EdDSA, ES256) and appends the complete JWT token ("header.payload.signature") to dst with zero heap allocations (when dst has sufficient capacity).
If signing fails, it returns nil and a typed error (ErrVerifierDestroyed, ErrNoSigningKey, ErrPayloadTooLarge, or ErrSigningFailed), leaving dst unmodified.
func (*Verifier) TrySignClaims ΒΆ added in v1.3.0
TrySignClaims formats a standard JSON payload `{"sub":"...","username":"...","iat":...,"exp":...}` into Scratch and appends the signed JWT token to dst with zero heap allocations.
It returns a typed error if the Verifier cannot sign, if RequireExp is set and exp <= 0, if sub is empty or exceeds 256 bytes (ClaimsLarge capacity limit), if username exceeds 256 bytes, or if the resulting payload exceeds MaxPayloadSize. (Note: standard Claims struct supports sub <= 64 bytes and username <= 128 bytes via Verify; callers verifying into standard Claims should remain within those bounds or use VerifyLarge).
func (*Verifier) TrySignClaimsWithScratch ΒΆ added in v1.3.0
func (v *Verifier) TrySignClaimsWithScratch(dst []byte, sub, username []byte, iat, exp int64, st *Scratch) ([]byte, error)
TrySignClaimsWithScratch is identical to TrySignClaims but uses caller-supplied Scratch state.
func (*Verifier) TrySignWithScratch ΒΆ added in v1.3.0
TrySignWithScratch is identical to TrySign but uses a caller-supplied Scratch state.
func (*Verifier) Verify ΒΆ
Verify validates the token signature, checks temporal ("exp", "nbf") and structural ("iss", "aud") claims against nowUnix, and populates out with the parsed claims. Safe for concurrent use across goroutines; performs 0 heap allocations (0 B/op, 0 allocs/op).
Example ΒΆ
ExampleVerifier_Verify demonstrates token verification with zero heap allocations, extracting standard claims, and checking sentinel errors using errors.Is.
package main
import (
"errors"
"fmt"
"github.com/KaikeGM/zerojwt"
)
func main() {
secret := []byte("super-secret-key-32-bytes-long!!")
verifier := zerojwt.NewVerifier(secret)
now := int64(1700000000)
// Issue a valid token into a stack-allocated buffer (0 allocs/op)
var tokBuf [512]byte
token := verifier.SignClaims(tokBuf[:0], []byte("user_123"), []byte("alice"), now, now+3600)
// Verify token into stack-allocated Claims
var claims zerojwt.Claims
if err := verifier.Verify(token, now, &claims); err != nil {
switch {
case errors.Is(err, zerojwt.ErrExpiredToken):
fmt.Println("Token is expired")
case errors.Is(err, zerojwt.ErrInvalidSignature):
fmt.Println("Invalid signature")
default:
fmt.Printf("Validation error: %v\n", err)
}
return
}
fmt.Printf("Authenticated: %s (%s)\n", claims.Username(), claims.Subject())
// Verifying an expired token triggers ErrExpiredToken
err := verifier.Verify(token, now+7200, &claims)
if errors.Is(err, zerojwt.ErrExpiredToken) {
fmt.Println("Detected expired token successfully")
}
}
Output: Authenticated: alice (user_123) Detected expired token successfully
func (*Verifier) VerifyBearer ΒΆ
VerifyBearer strips the "Bearer " prefix from an HTTP Authorization header and verifies the token with zero heap allocations.
func (*Verifier) VerifyCustom ΒΆ
func (v *Verifier) VerifyCustom(tok []byte, nowUnix int64, onClaim func(key, val []byte, isStr bool) bool) error
VerifyCustom validates the token signature, checks "exp", "nbf", "iss", and "aud" against nowUnix, and invokes onClaim(key, val, isString) for every top-level JSON claim in the payload. Performs 0 heap allocations. Note: key and val slices are valid only during the onClaim callback.
Example ΒΆ
ExampleVerifier_VerifyCustom demonstrates inspecting arbitrary custom JSON attributes and claims directly from the verified payload buffer in 0 allocations.
package main
import (
"fmt"
"github.com/KaikeGM/zerojwt"
)
func main() {
secret := []byte("super-secret-key-32-bytes-long!!")
verifier := zerojwt.NewVerifier(secret)
now := int64(1700000000)
var tokBuf [512]byte
b := verifier.NewSigner(tokBuf[:0])
b.SetSubject([]byte("user_789")).
SetClaimString("department", "engineering").
SetClaimInt("clearance_level", 5).
SetExpiresAt(now + 3600)
token, _ := b.Sign()
var dept string
var clearance int64
// Verify and extract arbitrary claims in a single pass with 0 heap allocations
err := verifier.VerifyCustom(token, now, func(key, val []byte, isStr bool) bool {
switch string(key) {
case "department":
dept = string(val)
case "clearance_level":
clearance, _ = zerojwt.ParsePositiveInt(val)
}
return true // continue scanning
})
if err != nil {
fmt.Printf("VerifyCustom error: %v\n", err)
return
}
fmt.Printf("Department: %s, Clearance Level: %d\n", dept, clearance)
}
Output: Department: engineering, Clearance Level: 5
func (*Verifier) VerifyLarge ΒΆ
func (v *Verifier) VerifyLarge(tok []byte, nowUnix int64, out *ClaimsLarge) error
VerifyLarge validates the token and populates a ClaimsLarge struct (256B buffers per claim).
func (*Verifier) VerifyLargeWithScratch ΒΆ
func (v *Verifier) VerifyLargeWithScratch(tok []byte, nowUnix int64, out *ClaimsLarge, st *Scratch) error
VerifyLargeWithScratch validates the token into ClaimsLarge using a caller-supplied Scratch.
func (*Verifier) VerifyWithScratch ΒΆ
VerifyWithScratch performs the same verification as Verify using a caller-supplied Scratch, avoiding sync.Pool.Get/Put in single-threaded event loops (such as gnet with LockOSThread).