Documentation
¶
Overview ¶
Package validator provides JWT validation using the lestrrat-go/jwx v3 library.
This package implements the ValidateToken interface required by the middleware and handles all aspects of JWT validation including signature verification, registered claims validation, and custom claims support.
Features ¶
- Signature verification using multiple algorithms (RS256, HS256, ES256, EdDSA, etc.)
- Validation of registered claims (iss, aud, exp, nbf, iat)
- Support for custom claims with validation logic
- Clock skew tolerance for time-based claims
- JWKS (JSON Web Key Set) support via key functions
- Multiple issuer and audience support
Supported Algorithms ¶
The validator supports 14 signature algorithms:
HMAC:
- HS256, HS384, HS512
RSA:
- RS256, RS384, RS512 (RSASSA-PKCS1-v1_5)
- PS256, PS384, PS512 (RSASSA-PSS)
ECDSA:
- ES256, ES384, ES512
- ES256K (secp256k1 curve)
EdDSA:
- EdDSA (Ed25519)
Basic Usage ¶
import (
"github.com/auth0/go-jwt-middleware/v3/validator"
"github.com/auth0/go-jwt-middleware/v3/jwks"
)
issuerURL, _ := url.Parse("https://auth.example.com/")
// Create JWKS provider
provider, err := jwks.NewCachingProvider(
jwks.WithIssuerURL(issuerURL),
)
if err != nil {
log.Fatal(err)
}
// Create validator
v, err := validator.New(
validator.WithKeyFunc(provider.KeyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer(issuerURL.String()),
validator.WithAudience("my-api"),
)
if err != nil {
log.Fatal(err)
}
// Validate token
claims, err := v.ValidateToken(ctx, tokenString)
if err != nil {
// Token invalid
}
// Type assert to ValidatedClaims
validatedClaims := claims.(*validator.ValidatedClaims)
Custom Claims ¶
Define custom claims by implementing the CustomClaims interface:
type MyCustomClaims struct {
Scope string `json:"scope"`
Permissions []string `json:"permissions"`
}
func (c *MyCustomClaims) Validate(ctx context.Context) error {
if c.Scope == "" {
return errors.New("scope is required")
}
return nil
}
// Use with validator
v, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer("https://issuer.example.com/"),
validator.WithAudience("my-api"),
validator.WithCustomClaims(func() *MyCustomClaims {
return &MyCustomClaims{}
}),
)
// Access custom claims
claims, _ := v.ValidateToken(ctx, tokenString)
validatedClaims := claims.(*validator.ValidatedClaims)
customClaims := validatedClaims.CustomClaims.(*MyCustomClaims)
fmt.Println(customClaims.Scope)
Multiple Issuers and Audiences ¶
Support tokens from multiple issuers or for multiple audiences:
v, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuers([]string{
"https://auth1.example.com/",
"https://auth2.example.com/",
}),
validator.WithAudiences([]string{
"api1",
"api2",
}),
)
Clock Skew Tolerance ¶
Allow time-based claims to be off by a certain duration:
v, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer("https://issuer.example.com/"),
validator.WithAudience("my-api"),
validator.WithAllowedClockSkew(30*time.Second),
)
This is useful when server clocks are slightly out of sync. Default: 0 (no clock skew allowed)
Using HMAC Algorithms ¶
For symmetric key algorithms (HS256, HS384, HS512):
secretKey := []byte("your-256-bit-secret")
keyFunc := func(ctx context.Context) (any, error) {
return secretKey, nil
}
v, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.HS256),
validator.WithIssuer("https://issuer.example.com/"),
validator.WithAudience("my-api"),
)
Using RSA Public Keys ¶
For asymmetric algorithms (RS256, PS256, ES256, etc.):
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
)
publicKeyPEM := []byte(`-----BEGIN PUBLIC KEY-----...`)
block, _ := pem.Decode(publicKeyPEM)
pubKey, _ := x509.ParsePKIXPublicKey(block.Bytes)
rsaPublicKey := pubKey.(*rsa.PublicKey)
keyFunc := func(ctx context.Context) (any, error) {
return rsaPublicKey, nil
}
v, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer("https://issuer.example.com/"),
validator.WithAudience("my-api"),
)
Validated Claims Structure ¶
The ValidatedClaims struct contains both registered and custom claims:
type ValidatedClaims struct {
RegisteredClaims RegisteredClaims // Standard JWT claims
CustomClaims CustomClaims // Your custom claims
}
type RegisteredClaims struct {
Issuer string // iss
Subject string // sub
Audience []string // aud
ID string // jti
Expiry int64 // exp (Unix timestamp)
NotBefore int64 // nbf (Unix timestamp)
IssuedAt int64 // iat (Unix timestamp)
}
Error Handling ¶
claims, err := v.ValidateToken(ctx, tokenString)
if err != nil {
// Token validation failed
// Possible reasons:
// - Invalid signature
// - Token expired
// - Token not yet valid
// - Invalid issuer
// - Invalid audience
// - Custom claims validation failed
}
Performance ¶
The validator is optimized for performance:
- Single-pass claim extraction
- Minimal memory allocations
- Direct JWT payload decoding for custom claims
- Efficient string comparison for issuer/audience
Typical validation time:
- With JWKS cache hit: <1ms
- With JWKS cache miss: 50-200ms (network fetch)
- HMAC validation: <0.1ms
- RSA validation: <0.5ms
Thread Safety ¶
The Validator is immutable after creation and safe for concurrent use. The same Validator instance can be used to validate multiple tokens concurrently.
Migration from go-jose v2 ¶
This package uses lestrrat-go/jwx v3 instead of square/go-jose v2. Key differences:
1. Better performance and security 2. More comprehensive algorithm support 3. Improved JWKS handling with automatic kid matching 4. Native Go 1.18+ generics support 5. Active maintenance and updates
The API is designed to be familiar to go-jose users while leveraging the improvements in jwx v3.
Index ¶
- Constants
- type ConfirmationClaim
- type CustomClaims
- type DPoPProofClaims
- func (d *DPoPProofClaims) GetATH() string
- func (d *DPoPProofClaims) GetHTM() string
- func (d *DPoPProofClaims) GetHTU() string
- func (d *DPoPProofClaims) GetIAT() int64
- func (d *DPoPProofClaims) GetJTI() string
- func (d *DPoPProofClaims) GetPublicKey() any
- func (d *DPoPProofClaims) GetPublicKeyThumbprint() string
- type Option
- func WithAlgorithm(algorithm SignatureAlgorithm) Option
- func WithAllowedClockSkew(skew time.Duration) Option
- func WithAudience(audience string) Option
- func WithAudiences(audiences []string) Option
- func WithCustomClaims[T CustomClaims](f func() T) Option
- func WithIssuer(issuerURL string) Option
- func WithIssuers(issuers []string) Option
- func WithKeyFunc(keyFunc func(context.Context) (any, error)) Option
- type RegisteredClaims
- type SignatureAlgorithm
- type ValidatedClaims
- type Validator
Constants ¶
const ( EdDSA = SignatureAlgorithm("EdDSA") HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256 HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384 HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512 RS256 = SignatureAlgorithm("RS256") // RSASSA-PKCS-v1.5 using SHA-256 RS384 = SignatureAlgorithm("RS384") // RSASSA-PKCS-v1.5 using SHA-384 RS512 = SignatureAlgorithm("RS512") // RSASSA-PKCS-v1.5 using SHA-512 ES256 = SignatureAlgorithm("ES256") // ECDSA using P-256 and SHA-256 ES384 = SignatureAlgorithm("ES384") // ECDSA using P-384 and SHA-384 ES512 = SignatureAlgorithm("ES512") // ECDSA using P-521 and SHA-512 ES256K = SignatureAlgorithm("ES256K") // ECDSA using secp256k1 curve and SHA-256 PS256 = SignatureAlgorithm("PS256") // RSASSA-PSS using SHA256 and MGF1-SHA256 PS384 = SignatureAlgorithm("PS384") // RSASSA-PSS using SHA384 and MGF1-SHA384 PS512 = SignatureAlgorithm("PS512") // RSASSA-PSS using SHA512 and MGF1-SHA512 )
Signature algorithms
const DPoPSupportedAlgorithms = "ES256 ES384 ES512 RS256 RS384 RS512 PS256 PS384 PS512 EdDSA"
DPoPSupportedAlgorithms is a space-separated list of supported DPoP algorithms for use in WWW-Authenticate headers per RFC 9449 Section 7.1.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ConfirmationClaim ¶
type ConfirmationClaim struct {
// JKT is the JWK SHA-256 Thumbprint (base64url-encoded).
// This thumbprint must match the JKT calculated from the DPoP proof's JWK.
JKT string `json:"jkt"`
}
ConfirmationClaim represents the cnf (confirmation) claim per RFC 7800 and RFC 9449. It contains the JWK SHA-256 thumbprint that binds the access token to a specific key pair. This is used for DPoP (Demonstrating Proof-of-Possession) token binding.
type CustomClaims ¶
CustomClaims defines any custom data / claims wanted. The Validator will call the Validate function which is where custom validation logic can be defined.
type DPoPProofClaims ¶
type DPoPProofClaims struct {
// JTI is a unique identifier for the DPoP proof JWT.
// Used for replay protection if nonce tracking is enabled.
JTI string `json:"jti"`
// HTM is the HTTP method (GET, POST, PUT, DELETE, etc.).
// Must match the actual HTTP request method (case-sensitive).
HTM string `json:"htm"`
// HTU is the HTTP URI (full URL of the request).
// Must match the actual request URL (scheme + host + path).
HTU string `json:"htu"`
// IAT is the time at which the DPoP proof was created (Unix timestamp).
// Must be fresh (within configured offset and leeway).
IAT int64 `json:"iat"`
// Nonce is an optional server-provided nonce for replay protection.
Nonce string `json:"nonce,omitempty"`
// ATH is an optional access token hash (base64url-encoded SHA-256).
// Used for additional binding in some implementations.
ATH string `json:"ath,omitempty"`
// PublicKey is the JWK extracted from the DPoP proof JWT header.
// Used to verify the proof's signature.
PublicKey any `json:"-"`
// PublicKeyThumbprint is the JKT calculated from the PublicKey.
// This is computed using SHA-256 thumbprint algorithm (RFC 7638).
// Must match the cnf.jkt from the access token.
PublicKeyThumbprint string `json:"-"`
}
DPoPProofClaims represents the claims in a DPoP proof JWT per RFC 9449. These claims are extracted from the JWT sent in the DPoP HTTP header.
func (*DPoPProofClaims) GetATH ¶
func (d *DPoPProofClaims) GetATH() string
GetATH returns the access token hash (ath) from the DPoP proof. This is an optional claim that binds the proof to a specific access token. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetHTM ¶
func (d *DPoPProofClaims) GetHTM() string
GetHTM returns the HTTP method (htm) from the DPoP proof. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetHTU ¶
func (d *DPoPProofClaims) GetHTU() string
GetHTU returns the HTTP URI (htu) from the DPoP proof. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetIAT ¶
func (d *DPoPProofClaims) GetIAT() int64
GetIAT returns the issued-at timestamp (iat) from the DPoP proof. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetJTI ¶
func (d *DPoPProofClaims) GetJTI() string
GetJTI returns the unique identifier (jti) of the DPoP proof. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetPublicKey ¶
func (d *DPoPProofClaims) GetPublicKey() any
GetPublicKey returns the public key from the DPoP proof's JWK. This method implements the core.DPoPProofClaims interface.
func (*DPoPProofClaims) GetPublicKeyThumbprint ¶
func (d *DPoPProofClaims) GetPublicKeyThumbprint() string
GetPublicKeyThumbprint returns the calculated JKT from the DPoP proof's JWK. This method implements the core.DPoPProofClaims interface.
type Option ¶
Option is how options for the Validator are set up. Options return errors to enable validation during construction.
func WithAlgorithm ¶
func WithAlgorithm(algorithm SignatureAlgorithm) Option
WithAlgorithm sets the signature algorithm that tokens must use. This is a required option.
Supported algorithms: RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, HS256, HS384, HS512, EdDSA.
func WithAllowedClockSkew ¶
WithAllowedClockSkew sets the allowed clock skew for time-based claims.
This allows for some tolerance when validating exp, nbf, and iat claims to account for clock differences between systems. If not set, the default is 0 (no clock skew allowed).
func WithAudience ¶
WithAudience sets a single expected audience claim (aud) for token validation. This is a required option (use either WithAudience or WithAudiences, not both).
The audience should match one of the aud claims in the JWT. Tokens without a matching audience will be rejected.
func WithAudiences ¶
WithAudiences sets multiple expected audience claims (aud) for token validation. This is a required option (use either WithAudience or WithAudiences, not both).
The token must contain at least one of the specified audiences. Tokens without any matching audience will be rejected.
func WithCustomClaims ¶
func WithCustomClaims[T CustomClaims](f func() T) Option
WithCustomClaims sets a function that returns a CustomClaims object for unmarshalling and validation.
The function is called during construction to validate it returns a non-nil value, and then called for each token validation to create a new instance.
Using generics allows you to return your concrete claims type directly without needing to explicitly cast to the CustomClaims interface.
IMPORTANT: The function must be:
- Thread-safe (called concurrently by multiple requests)
- Idempotent (returns a new instance each time, no shared state)
- Fast (called on every token validation)
- Panic-free (panics will crash the request handler)
Example:
validator.New(
// ... other options
validator.WithCustomClaims(func() *MyClaims {
return &MyClaims{} // No interface cast needed
}),
)
func WithIssuer ¶
WithIssuer sets a single expected issuer claim (iss) for token validation. This is a required option (use either WithIssuer or WithIssuers, not both).
The issuer URL should match the iss claim in the JWT. Tokens with a different issuer will be rejected.
func WithIssuers ¶
WithIssuers sets multiple expected issuer claims (iss) for token validation. This is a required option (use either WithIssuer or WithIssuers, not both).
The token must contain one of the specified issuers. Tokens without any matching issuer will be rejected.
func WithKeyFunc ¶
WithKeyFunc sets the function that provides the key for token verification. This is a required option.
The keyFunc is called during token validation to retrieve the key(s) used to verify the token signature. For JWKS-based validation, use jwks.Provider.KeyFunc.
type RegisteredClaims ¶
type RegisteredClaims struct {
Issuer string `json:"iss,omitempty"`
Subject string `json:"sub,omitempty"`
Audience []string `json:"aud,omitempty"`
Expiry int64 `json:"exp,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
ID string `json:"jti,omitempty"`
}
RegisteredClaims represents public claim values (as specified in RFC 7519).
type SignatureAlgorithm ¶
type SignatureAlgorithm string
SignatureAlgorithm is a signature algorithm.
type ValidatedClaims ¶
type ValidatedClaims struct {
CustomClaims CustomClaims
RegisteredClaims RegisteredClaims
// ConfirmationClaim contains the cnf claim for DPoP binding (RFC 7800, RFC 9449).
// This field will be nil for Bearer tokens and populated for DPoP tokens.
ConfirmationClaim *ConfirmationClaim `json:"cnf,omitempty"`
}
ValidatedClaims is the struct that will be inserted into the context for the user. CustomClaims will be nil unless WithCustomClaims is passed to New.
func (*ValidatedClaims) GetConfirmationJKT ¶
func (v *ValidatedClaims) GetConfirmationJKT() string
GetConfirmationJKT returns the jkt from the cnf claim, or empty string if not present. This method implements the core.TokenClaims interface.
func (*ValidatedClaims) HasConfirmation ¶
func (v *ValidatedClaims) HasConfirmation() bool
HasConfirmation returns true if the token has a cnf claim. This method implements the core.TokenClaims interface.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator validates JWTs using the jwx v3 library.
func New ¶
New creates a new Validator with the provided options.
Required options:
- WithKeyFunc: Function to provide verification key(s)
- WithAlgorithm: Signature algorithm to validate
- WithIssuer: Expected issuer claim (iss)
- WithAudience or WithAudiences: Expected audience claim(s) (aud)
Optional options:
- WithCustomClaims: Custom claims validation
- WithAllowedClockSkew: Clock skew tolerance for time-based claims
Example:
validator, err := validator.New(
validator.WithKeyFunc(keyFunc),
validator.WithAlgorithm(validator.RS256),
validator.WithIssuer("https://issuer.example.com/"),
validator.WithAudience("my-api"),
validator.WithAllowedClockSkew(30*time.Second),
)
if err != nil {
log.Fatal(err)
}
func (*Validator) ValidateDPoPProof ¶
func (v *Validator) ValidateDPoPProof(ctx context.Context, proofString string) (*DPoPProofClaims, error)
ValidateDPoPProof validates a DPoP proof JWT and returns the extracted claims. It verifies the JWT signature using the embedded JWK and calculates the JKT.
This method performs the following validations per RFC 9449:
- Parses the DPoP proof JWT
- Verifies the typ header is "dpop+jwt"
- Extracts the JWK from the JWT header
- Verifies the JWT signature using the embedded JWK
- Extracts required claims (jti, htm, htu, iat)
- Calculates the JKT (JWK thumbprint) using SHA-256
The method does NOT validate:
- htm matches HTTP method (done in core)
- htu matches request URL (done in core)
- iat freshness (done in core)
- JKT matches cnf.jkt from access token (done in core)
This separation ensures the validator remains a pure JWT validation library with no knowledge of HTTP requests or transport concerns.