jwt

package module
v0.1.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2025 License: MIT Imports: 31 Imported by: 88

README

JWT

build status gocov report card godocs

Zero-dependency lighweight, fast and simple JWT & JWKS implementation written in Go. This package was designed with security, performance and simplicity in mind, it protects your tokens from critical vulnerabilities that you may find in other libraries.

Benchmarks Total Repetitions - higher is better

Please star this open source project to attract more developers so that together we can improve it even more!

Installation

The only requirement is the Go Programming Language.

$ go get github.com/kataras/jwt@latest

Import as import "github.com/kataras/jwt" and use it as jwt.XXX.

Table of Contents

Getting Started

Sign and generate a token with the Sign method, returns the token in compact form. Optionally set an expiration, if "exp" is missing from the payload use the jwt.MaxAge helper. Verify the token with the Verify method, returns a VerifiedToken value. Decode the custom claims with the VerifiedToken.Claims method. Extremely easy!

package main

import (
	"time"

	"github.com/kataras/jwt"
)

// Keep it secret.
var sharedKey = []byte("sercrethatmaycontainch@r$32chars")

type FooClaims struct {
	Foo string `json:"foo"`
}

func main() {
	// Generate a token which expires at 15 minutes from now:
	myClaims := FooClaims{
		Foo: "bar",
	} // can be a map too.

	token, err := jwt.Sign(jwt.HS256, sharedKey, myClaims, jwt.MaxAge(15*time.Minute))
	if err != nil {
		panic(err)
	}

	// Verify and extract claims from a token:
	verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token)
	if err != nil {
		panic(err)
	}

	var claims FooClaims
	err = verifiedToken.Claims(&claims)
	if err != nil {
		panic(err)
	}

	print(claims.Foo)
}

The package contains comments on each one of its exported functions, structures and variables, therefore, for a more detailed technical documentation please refer to godocs.

Sign a Token

Signing and Verifying a token is an extremely easy process.

Signing a Token is done through the Sign package-level function.

var sharedKey = []byte("sercrethatmaycontainch@r$32chars")
type User struct {
    Username string `json:"username"`
}
userClaims := User {
    Username:"kataras",
}

token, err := jwt.Sign(jwt.HS256, sharedkey, userClaims, jwt.MaxAge(15 *time.Minute))

[1] The first argument is the signing algorithm to create the signature part. [2] The second argument is the private key (or shared key, when symmetric algorithm was chosen) will be used to create the signature. [3] The third argument is the JWT claims. The JWT claims is the payload part and it depends on your application's requirements, there you can set custom fields (and expiration) that you can extract to another request of the same authorized client later on. Note that the claims can be any Go type, including custom struct, map and raw []byte. [4] The last variadic argument is a type of SignOption (MaxAge function and Claims struct are both valid sign options), can be used to merge custom claims with the standard ones. Returns the encoded token, ready to be sent and stored to the client.

The jwt.MaxAge is a helper which sets the jwt.Claims.Expiry and jwt.Claims.IssuedAt for you.

Example Code to manually set all claims using a standard map:

now := time.Now()
claims := map[string]any{
    "iat": now.Unix(),
    "exp": now.Add(15 * time.Minute).Unix(),
    "foo": "bar",
}

token, err := jwt.Sign(jwt.HS256, sharedKey, claims)

See SignWithHeader too.

Example Code to merge map claims with standard claims:

customClaims := jwt.Map{"foo": "bar"}

now := time.Now()
standardClaims := jwt.Claims{
    Expiry:   now.Add(15 * time.Minute).Unix(),
    IssuedAt: now.Unix(), 
    Issuer:   "my-app",
}

token, err := jwt.Sign(jwt.HS256, sharedKey, customClaims, standardClaims)

The jwt.Map is just a type alias, a shortcut, of map[string]any.

At all cases, the iat(IssuedAt) and exp(Expiry/MaxAge) (and nbf(NotBefore)) values will be validated automatically on the Verify method.

Example Code to Sign & Verify a non-JSON payload:

token, err := jwt.Sign(jwt.HS256, sharedkey, []byte("raw payload - no json here"))

If the payload is not a JSON one, then merging with standard claims is not possible, therefore options like jwt.MaxAge are not available.

verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token, jwt.Plain)
// verifiedToken.Payload == raw contents

Again, if the received payload is not a JSON one, options like jwt.Expected or jwt.NewBlocklist are not available as well.

The standard JWT Claims

The jwt.Claims we've shown above, looks like this:

type Claims struct {
    // The opposite of the exp claim. A number representing a specific
    // date and time in the format “seconds since epoch” as defined by POSIX.
    // This claim sets the exact moment from which this JWT is considered valid.
    // The current time (see `Clock` package-level variable)
    // must be equal to or later than this date and time.
    NotBefore int64 `json:"nbf,omitempty"`

    // A number representing a specific date and time (in the same
    // format as exp and nbf) at which this JWT was issued.
    IssuedAt int64 `json:"iat,omitempty"`

    // A number representing a specific date and time in the
    // format “seconds since epoch” as defined by POSIX6.
    // This claims sets the exact moment from which
    // this JWT is considered invalid. This implementation
    // allow for a certain skew between clocks
    // (by considering this JWT to be valid for a few minutes
    // after the expiration date, modify the `Clock` variable).
    Expiry int64 `json:"exp,omitempty"`

    // A string representing a unique identifier for this JWT.
    // This claim may be used to differentiate JWTs with
    // other similar content (preventing replays, for instance).
    ID string `json:"jti,omitempty"`

    // A string or URI that uniquely identifies the party
    // that issued the JWT.
    // Its interpretation is application specific
    // (there is no central authority managing issuers).
    Issuer string `json:"iss,omitempty"`

    // A string or URI that uniquely identifies the party
    // that this JWT carries information about.
    // In other words, the claims contained in this JWT
    // are statements about this party.
    // The JWT spec specifies that this claim must be unique in
    // the context of the issuer or,
    // in cases where that is not possible, globally unique. Handling of
    // this claim is application specific.
    Subject string `json:"sub,omitempty"`

    // Either a single string or URI or an array of such
    // values that uniquely identify the intended recipients of this JWT.
    // In other words, when this claim is present, the party reading
    // the data in this JWT must find itself in the aud claim or
    // disregard the data contained in the JWT.
    // As in the case of the iss and sub claims, this claim is
    // application specific.
    Audience []string `json:"aud,omitempty"`
}

Verify a Token

Verifying a Token is done through the Verify package-level function.

verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token)

See VerifyWithHeaderValidator too.

The VerifiedToken carries the token decoded information:

type VerifiedToken struct {
    Token          []byte // The original token.
    Header         []byte // The header (decoded) part.
    Payload        []byte // The payload (decoded) part.
    Signature      []byte // The signature (decoded) part.
    StandardClaims Claims // Standard claims extracted from the payload.
}
Decode custom Claims

To extract any custom claims, given on the Sign method, we use the result of the Verify method, which is a VerifiedToken pointer. This VerifiedToken has a single method, the Claims(dest any) error one, which can be used to decode the claims (payload part) to a value of our choice. Again, that value can be a map or any struct.

var claims = struct {
    Foo string `json:"foo"`
}{} // or a map.

err := verifiedToken.Claims(&claims)

By default expiration set and validation is done through time.Now(). You can change that behavior through the jwt.Clock variable, e.g.

jwt.Clock = time.Now().UTC
JSON required tag

When more than one token with different claims can be generated based on the same algorithm and key, somehow you need to invalidate a token if its payload misses one or more fields of your custom claims structure. Although it's not recommended to use the same algorithm and key for generating two different types of tokens, you can do it, and to avoid invalid claims to be retrieved by your application's route handler this package offers the JSON ,required tag field. It checks if the claims extracted from the token's payload meet the requirements of the expected struct value.

The first thing we have to do is to change the default jwt.Unmarshal variable to the jwt.UnmarshalWithRequired, once at the init of the application:

func init() {
    jwt.Unmarshal = jwt.UnmarshalWithRequired
}

The second thing, is to add the ,required json tag field to our struct, e.g.

type userClaims struct {
    Username string `json:"username,required"`
}

That's all, the VerifiedToken.Claims method will throw an ErrMissingKey if the given token's payload does not meet the requirements.

Standard Claims Validators

A more performance-wise alternative to json:"XXX,required" is to add validators to check the standard claims values through a TokenValidator or to check the custom claims manually after the VerifiedToken.Claims method.

The TokenValidator interface looks like this:

type TokenValidator interface {
	ValidateToken(token []byte, standardClaims Claims, err error) error
}

The last argument of Verify/VerifyEncrypted optionally accepts one or more TokenValidator. Available builtin validators:

  • Leeway(time.Duration)
  • Expected
  • Blocklist

The Leeway adds validation for a leeway expiration time. If the token was not expired then a comparison between this "leeway" and the token's "exp" one is expected to pass instead (now+leeway > exp). Example of use case: disallow tokens that are going to be expired in 3 seconds from now, this is useful to make sure that the token is valid when the when the user fires a database call:

verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token, jwt.Leeway(3*time.Second))
if err != nil {
   // err == jwt.ErrExpired
}

The Expected performs simple checks between standard claims values. For example, disallow tokens that their "iss" claim does not match the "my-app" value:

verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token, jwt.Expected{
    Issuer: "my-app",
})
if err != nil {
    // errors.Is(jwt.ErrExpected, err)
}

Block a Token

When a user logs out, the client app should delete the token from its memory. This would stop the client from being able to make authorized requests. But if the token is still valid and somebody else has access to it, the token could still be used. Therefore, a server-side invalidation is indeed useful for cases like that. When the server receives a logout request, take the token from the request and store it to the Blocklist through its InvalidateToken method. For each authorized request the jwt.Verify will check the Blocklist to see if the token has been invalidated. To keep the search space small, the expired tokens are automatically removed from the Blocklist's in-memory storage.

Enable blocklist by following the three simple steps below.

1. Initialize a blocklist instance, clean unused and expired tokens every 1 hour.

blocklist := jwt.NewBlocklist(1 * time.Hour)

2. Add the blocklist instance to the jwt.Verify's last argument, to disallow blocked entries.

verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token, blocklist)
// [err == jwt.ErrBlocked when the token is valid but was blocked]

3. Call the blocklist.InvalidateToken whenever you want to block a specific authorized token. The method accepts the token and the expiration time should be removed from the blocklist.

blocklist.InvalidateToken(verifiedToken.Token, verifiedToken.StandardClaims)

By default the unique identifier is retrieved through the "jti" (Claims{ID}) and if that it's empty then the raw token is used as the map key instead. To change that behavior simply modify the blocklist.GetKey field before the InvalidateToken method.

Token Pair

A Token pair helps us to handle refresh tokens. It is a structure which holds both Access Token and Refresh Token. Refresh Token is long-live and access token is short-live. The server sends both of them at the first contact. The client uses the access token to access an API. The client can renew its access token by hitting a special REST endpoint to the server. The server verifies the refresh token and optionally the access token which should return ErrExpired, if it's expired or going to be expired in some time from now (Leeway), and renders a new generated token to the client. There are countless resources online and different kind of methods for using a refresh token. This jwt package offers just a helper structure which holds both the access and refresh tokens and it's ready to be sent and received to and from a client.

type ClientClaims struct {
    ClientID string `json:"client_id"`
}
accessClaims := ClientClaims{ClientID: "client-id"}
accessToken, err := jwt.Sign(alg, secret, accessClaims, jwt.MaxAge(10*time.Minute))
if err != nil {
    // [handle error...]
}

refreshClaims := jwt.Claims{Subject: "client", Issuer: "my-app"}
refreshToken, err := jwt.Sign(alg, secret, refreshClaims, jwt.MaxAge(time.Hour))
if err != nil {
    // [handle error...]
}

tokenPair := jwt.NewTokenPair(accessToken, refreshToken)

The tokenPair is JSON-compatible value, you can render it to a client and read it from a client HTTP request.

JSON Web Algorithms

There are several types of signing algorithms available according to the JWA(JSON Web Algorithms) spec. The specification requires a single algorithm to be supported by all conforming implementations:

  • HMAC using SHA-256, called HS256 in the JWA spec.

The specification also defines a series of recommended algorithms:

  • RSASSA PKCS1 v1.5 using SHA-256, called RS256 in the JWA spec.
  • ECDSA using P-256 and SHA-256, called ES256 in the JWA spec.

The implementation supports all of the above plus RSA-PSS and the new Ed25519. Navigate to the alg.go source file for details. In-short:

Algorithm jwt.Sign jwt.Verify
jwt.HS256 / HS384 / HS512 []byte The same sign key
jwt.RS256 / RS384 / RS512 *rsa.PrivateKey *rsa.PublicKey
jwt.PS256 / PS384 / PS512 *rsa.PrivateKey *rsa.PublicKey
jwt.ES256 / ES384 / ES512 *ecdsa.PrivateKey *ecdsa.PublicKey
jwt.EdDSA ed25519.PrivateKey ed25519.PublicKey
Choose the right Algorithm

Choosing the best algorithm for your application needs is up to you, however, my recommendations follows.

  • Already work with RSA public and private keys? Choose RSA(RS256/RS384/RS512/PS256/PS384/PS512) (length of produced token characters is bigger).
  • If you need the separation between public and private key, choose ECDSA(ES256/ES384/ES512) or EdDSA. ECDSA and EdDSA produce smaller tokens than RSA.
  • If you need performance and well-tested algorithm, choose HMAC(HS256/HS384/HS512) - the most common method.

The basic difference between symmetric and an asymmetric algorithm is that symmetric uses one shared key for both signing and verifying a token, and the asymmetric uses private key for signing and a public key for verifying. In general, asymmetric data is more secure because it uses different keys for the signing and verifying process but it's slower than symmetric ones.

Use your own Algorithm

If you ever need to use your own JSON Web algorithm, just implement the Alg interface. Pass it on jwt.Sign and jwt.Verify functions and you're ready to GO.

Generate keys

Keys can be generated via OpenSSL or through Go's standard library.

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/elliptic"
    "crypto/ed25519"
)
// Generate HMAC
sharedKey := make([]byte, 32)
_, _ = rand.Read(sharedKey)

// Generate RSA
bitSize := 2048
privateKey, _ := rsa.GenerateKey(rand.Reader, bitSize)
publicKey := &privateKey.PublicKey

// Generace ECDSA
c := elliptic.P256()
privateKey, _ := ecdsa.GenerateKey(c, rand.Reader)
publicKey := &privateKey.PublicKey

// Generate EdDSA
publicKey, privateKey, _ := ed25519.GenerateKey(rand.Reader)

Converting keys to PEM files is kind of easy task using the Go Programming Language, take a quick look at the PEM example for ed25519.

Load and Parse keys

This package contains all the helpers you need to load and parse PEM-formatted keys.

All the available helpers:

// HMAC
MustLoadHMAC(filenameOrRaw string) []byte
// RSA
MustLoadRSA(privFile, pubFile string) (*rsa.PrivateKey, *rsa.PublicKey)
LoadPrivateKeyRSA(filename string) (*rsa.PrivateKey, error)
LoadPublicKeyRSA(filename string) (*rsa.PublicKey, error) 
ParsePrivateKeyRSA(key []byte) (*rsa.PrivateKey, error)
ParsePublicKeyRSA(key []byte) (*rsa.PublicKey, error)
// ECDSA
MustLoadECDSA(privFile, pubFile string) (*ecdsa.PrivateKey, *ecdsa.PublicKey)
LoadPrivateKeyECDSA(filename string) (*ecdsa.PrivateKey, error)
LoadPublicKeyECDSA(filename string) (*ecdsa.PublicKey, error) 
ParsePrivateKeyECDSA(key []byte) (*ecdsa.PrivateKey, error)
ParsePublicKeyECDSA(key []byte) (*ecdsa.PublicKey, error)
// EdDSA
MustLoadEdDSA(privFile, pubFile string) (ed25519.PrivateKey, ed25519.PublicKey)
LoadPrivateKeyEdDSA(filename string) (ed25519.PrivateKey, error)
LoadPublicKeyEdDSA(filename string) (ed25519.PublicKey, error)
ParsePrivateKeyEdDSA(key []byte) (ed25519.PrivateKey, error)
ParsePublicKeyEdDSA(key []byte) (ed25519.PublicKey, error)

Example Code:

import "github.com/kataras/jwt"
privateKey, publicKey := jwt.MustLoadEdDSA("./private_key.pem", "./public_key.pem")
claims := jwt.Map{"foo": "bar"}
maxAge := jwt.MaxAge(15 * time.Minute)

token, err := jwt.Sign(jwt.EdDSA, privateKey, claims, maxAge)
verifiedToken, err := Verify(EdDSA, publicKey, token)

Embedded keys? No problem, just integrate the jwt.ReadFile variable which is just a type of func(filename string) ([]byte, error).

Encryption

JWE (encrypted JWTs) is outside the scope of this package, a wire encryption of the token's payload is offered to secure the data instead. If the application requires to transmit a token which holds private data then it needs to encrypt the data on Sign and decrypt on Verify. The SignEncrypted and VerifyEncrypted package-level functions can be called to apply any type of encryption.

The package offers one of the most popular and common way to secure data; the GCM mode + AES cipher. We follow the encrypt-then-sign flow which most researchers recommend (it's safer as it prevents padding oracle attacks).

In-short, you need to call the jwt.GCM and pass its result to the jwt.SignEncrypted and jwt.VerifyEncrypted:

// Replace with your own keys and keep them secret.
// The "encKey" is used for the encryption and
// the "sigKey" is used for the selected JSON Web Algorithm
// (shared/symmetric HMAC in that case).
var (
    encKey = MustGenerateRandom(32)
    sigKey = MustGenerateRandom(32)
)

func main(){
    encrypt, decrypt, err := GCM(encKey, nil)
    if err != nil {
        // [handle error...]
    }
    // Encrypt and Sign the claims:
    token, err := SignEncrypted(jwt.HS256, sigKey, encrypt, claims, jwt.MaxAge(15 * time.Minute))
    // [...]

    // Verify and decrypt the claims:
    verifiedToken, err := VerifyEncrypted(jwt.HS256, sigKey, decrypt, token)
    // [...]
}

Read more about GCM at: https://en.wikipedia.org/wiki/Galois/Counter_Mode

References

Here is what helped me to implement JWT in Go:

License

This software is licensed under the MIT License.

Documentation

Overview

Package jwt provides a comprehensive, high-performance implementation of JSON Web Tokens (JWT) as defined in RFC 7519, with full support for JSON Web Algorithms (JWA) from RFC 7518.

Overview

This library delivers a complete JWT solution with emphasis on security, performance, and ease of use. It supports all major cryptographic algorithms, provides extensive validation capabilities, and offers flexible APIs for both simple and advanced use cases.

Key Features

• **Algorithm Support**: Complete implementation of JWT algorithms

  • HMAC: HS256, HS384, HS512 (symmetric)
  • RSA: RS256, RS384, RS512 (PKCS#1 v1.5 padding)
  • RSA-PSS: PS256, PS384, PS512 (PSS padding)
  • ECDSA: ES256, ES384, ES512 (P-256, P-384, P-521 curves)
  • EdDSA: Ed25519 (modern elliptic curve)
  • None: Unsecured tokens (for testing only)

• **JSON Web Key Set (JWKS)**: Full RFC 7517 compliance

  • Fetch public keys from remote endpoints
  • Multi-key verification and key rotation
  • Support for Auth0, AWS Cognito, Google, Microsoft
  • Automatic key conversion and validation

• **Claims Validation**: Comprehensive RFC 7519 compliance

  • Standard claims: exp, nbf, iat, iss, sub, aud, jti
  • Custom claim validation with extensible framework
  • Time-based validation with configurable leeway
  • Audience validation with security best practices

• **Performance**: Optimized for high-throughput applications

  • ~3x faster than comparable libraries
  • Zero-allocation parsing paths
  • Efficient memory usage patterns
  • Minimal runtime overhead

• **Security**: Enterprise-grade security features

  • Constant-time signature verification
  • Algorithm confusion attack prevention
  • Timing attack resistance
  • Secure random number generation

Architecture

The library is built around several core concepts:

**Token Lifecycle**:

  1. Sign() - Create and sign JWT tokens with claims
  2. Verify() - Validate signatures and extract claims
  3. Claims processing - Type-safe claim extraction

**Key Management**:

  • Single keys for simple scenarios
  • Multi-key maps for key rotation
  • JWKS integration for dynamic key fetching
  • Key ID (kid) based key selection

**Validation Framework**:

  • Built-in standard claims validation
  • Extensible TokenValidator interface
  • Composable validation chains
  • Custom validation logic support

Quick Start

## Basic HMAC Usage

package main

import (
    "fmt"
    "time"
    "github.com/kataras/jwt"
)

func main() {
    // Secret key for HMAC (keep secure in production)
    secretKey := []byte("your-256-bit-secret-key-here")

    // Create claims
    myClaims := map[string]any{
        "user_id": 12345,
        "role":    "admin",
        "email":   "user@example.com",
    }

    // Sign token with 15-minute expiration
    token, err := jwt.Sign(jwt.HS256, secretKey, myClaims, jwt.MaxAge(15*time.Minute))
    if err != nil {
        panic(err)
    }

    fmt.Printf("Token: %s\n", string(token))

    // Verify and extract claims
    verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, token)
    if err != nil {
        panic(err)
    }

    var claims map[string]any
    err = verifiedToken.Claims(&claims)
    if err != nil {
        panic(err)
    }

    fmt.Printf("User ID: %.0f\n", claims["user_id"])
    fmt.Printf("Role: %s\n", claims["role"])
}

## RSA Public Key Usage

// Load RSA key pair
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
publicKey := &privateKey.PublicKey

// Sign with private key
token, err := jwt.Sign(jwt.RS256, privateKey, claims)

// Verify with public key
verifiedToken, err := jwt.Verify(jwt.RS256, publicKey, token)

## Multi-Key Verification with JWKS

// Fetch keys from identity provider
keys, err := jwt.FetchPublicKeys("https://auth.example.com/.well-known/jwks.json")
if err != nil {
    log.Fatal(err)
}

// Verify token with automatic key selection
verifiedToken, err := jwt.Verify(jwt.RS256, keys, token)

## Advanced Claims Validation

// Create custom validator
customValidator := jwt.TokenValidatorFunc(func(token []byte, claims jwt.Claims, err error) error {
    if err != nil {
        return err
    }

    // Custom business logic validation
    if claims.Subject != "expected-user" {
        return errors.New("invalid user")
    }

    return nil
})

// Verify with multiple validators
verifiedToken, err := jwt.Verify(jwt.RS256, publicKey, token,
    jwt.Expected{Issuer: "trusted-issuer"},
    jwt.MaxAge(time.Hour),
    customValidator,
)

Standard Claims

The library provides full support for RFC 7519 standard claims:

• **exp** (Expiration Time): Token expiry validation • **nbf** (Not Before): Token validity start time • **iat** (Issued At): Token creation time • **iss** (Issuer): Token issuer validation • **sub** (Subject): Token subject identification • **aud** (Audience): Intended token audience • **jti** (JWT ID): Unique token identifier

Example with standard claims:

claims := jwt.Claims{
    Issuer:    "myapp.com",
    Subject:   "user123",
    Audience:  []string{"api.myapp.com"},
    ExpiresAt: time.Now().Add(time.Hour).Unix(),
    NotBefore: time.Now().Unix(),
    IssuedAt:  time.Now().Unix(),
    ID:        "unique-token-id",
}

token, err := jwt.Sign(jwt.HS256, secretKey, claims)

Security Best Practices

**Algorithm Selection**:

  • Use HS256 for shared secret scenarios
  • Use RS256 or ES256 for public key scenarios
  • Consider EdDSA for modern high-performance applications
  • Never use "none" algorithm in production

**Key Management**:

  • Use keys with sufficient entropy (256 bits minimum)
  • Rotate keys regularly (monthly/quarterly)
  • Store private keys securely (HSM, key vault)
  • Use different keys for different applications

**Validation**:

  • Always validate exp, nbf, iat claims
  • Implement strict audience validation
  • Use issuer validation for trusted sources
  • Implement rate limiting for token endpoints

**Implementation**:

  • Validate tokens on every request
  • Use HTTPS for all token transmission
  • Implement proper error handling
  • Log security events for monitoring

Performance Characteristics

This library is optimized for high-throughput applications:

**Benchmarks** (compared to similar libraries):

  • Sign operations: ~3x faster
  • Verify operations: ~3x faster
  • Memory allocations: ~50% fewer
  • CPU usage: ~60% lower

**Optimizations**:

  • Zero-allocation parsing for common cases
  • Efficient base64url encoding/decoding
  • Optimized JSON marshaling/unmarshaling
  • Minimal reflection usage

Integration Patterns

## Web Middleware

func JWTMiddleware(secretKey []byte) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            auth := r.Header.Get("Authorization")
            if !strings.HasPrefix(auth, "Bearer ") {
                http.Error(w, "Unauthorized", http.StatusUnauthorized)
                return
            }

            token := []byte(strings.TrimPrefix(auth, "Bearer "))
            verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, token)
            if err != nil {
                http.Error(w, "Invalid token", http.StatusUnauthorized)
                return
            }

            // Add claims to request context
            ctx := context.WithValue(r.Context(), "claims", verifiedToken.StandardClaims)
            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
}

## API Gateway Integration

// Configure for AWS API Gateway, Kong, etc.
type TokenValidator struct {
    jwksURL string
    keys    jwt.Keys
    lastFetch time.Time
}

func (v *TokenValidator) ValidateToken(tokenString string) (*jwt.VerifiedToken, error) {
    // Refresh keys if needed
    if time.Since(v.lastFetch) > time.Hour {
        keys, err := jwt.FetchPublicKeys(v.jwksURL)
        if err == nil {
            v.keys = keys
            v.lastFetch = time.Now()
        }
    }

    return jwt.Verify(jwt.RS256, v.keys, []byte(tokenString))
}

Error Handling

The library provides detailed error information for debugging and monitoring:

verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, token)
if err != nil {
    switch err {
    case jwt.ErrTokenForm:
        // Malformed token structure
    case jwt.ErrTokenAlg:
        // Algorithm mismatch or unsupported
    case jwt.ErrExpired:
        // Token has expired
    case jwt.ErrNotValidYet:
        // Token not valid yet (nbf claim)
    default:
        // Other errors (signature, parsing, etc.)
    }
}

Testing Support

The library includes comprehensive testing utilities:

// Mock HTTP client for JWKS testing
mockClient := &MockHTTPClient{
    Response: &http.Response{
        StatusCode: 200,
        Body: ioutil.NopCloser(strings.NewReader(jwksJSON)),
    },
}

jwks, err := jwt.FetchJWKS(mockClient, "https://example.com/.well-known/jwks.json")

**Project Home**: https://github.com/kataras/jwt

**Examples**: https://github.com/kataras/jwt/tree/main/_examples

  • Basic usage examples
  • Advanced validation scenarios
  • Integration patterns
  • Real-world applications

**Benchmarks**: https://github.com/kataras/jwt/tree/main/_benchmarks

  • Performance comparisons
  • Memory usage analysis
  • Throughput measurements

**Documentation**: https://pkg.go.dev/github.com/kataras/jwt

  • Complete API reference
  • Function documentation
  • Type definitions

Standards Compliance

This library implements the following RFCs and standards:

• **RFC 7519**: JSON Web Token (JWT) • **RFC 7515**: JSON Web Signature (JWS) • **RFC 7516**: JSON Web Encryption (JWE) - partial • **RFC 7517**: JSON Web Key (JWK) • **RFC 7518**: JSON Web Algorithms (JWA) • **RFC 8037**: CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JSON Object Signing and Encryption (JOSE)

The implementation is tested against official test vectors and interoperates with major JWT libraries and identity providers.

Index

Constants

View Source
const NoPadding = base64.NoPadding

NoPadding is an alias for base64.NoPadding for convenience.

Variables

View Source
var (
	// ErrTokenSignature indicates that JWT signature verification has failed.
	//
	// This error is returned when the computed signature does not match the
	// signature provided in the JWT token. It indicates that either:
	//   - The token has been tampered with
	//   - The wrong key was used for verification
	//   - The token was signed with a different algorithm
	//   - The signature is corrupted or malformed
	//
	// **Security Implications**: This error should be treated as a security
	// event and may indicate an attack attempt. Always log these failures
	// for security monitoring.
	//
	// **Common Causes**:
	//   - Using wrong verification key
	//   - Algorithm mismatch between signing and verification
	//   - Token tampering or corruption
	//   - Clock skew causing timing-related signature issues
	//
	// Example usage:
	//
	//	verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, token)
	//	if err == jwt.ErrTokenSignature {
	//	    log.Printf("Security alert: Invalid token signature from %s", clientIP)
	//	    http.Error(w, "Unauthorized", http.StatusUnauthorized)
	//	    return
	//	}
	ErrTokenSignature = errors.New("jwt: invalid token signature")

	// ErrInvalidKey indicates that the provided key is not valid for the algorithm.
	//
	// This error occurs when the key type doesn't match what the algorithm expects.
	// Each algorithm has specific key type requirements that must be satisfied
	// for proper cryptographic operations.
	//
	// **Algorithm Key Requirements**:
	//   - HMAC (HS256/384/512): []byte (shared secret)
	//   - RSA (RS256/384/512, PS256/384/512): *rsa.PrivateKey (sign), *rsa.PublicKey (verify)
	//   - ECDSA (ES256/384/512): *ecdsa.PrivateKey (sign), *ecdsa.PublicKey (verify)
	//   - EdDSA: ed25519.PrivateKey (sign), ed25519.PublicKey (verify)
	//
	// **Common Scenarios**:
	//   - Passing string instead of []byte for HMAC
	//   - Using wrong key type for asymmetric algorithms
	//   - Attempting to sign with public key or verify with private key incorrectly
	//   - Using keys from different cryptographic families
	//
	// **Prevention**: Always ensure key types match algorithm requirements
	// and validate keys before use in production systems.
	//
	// Example usage:
	//
	//	// Wrong: string instead of []byte for HMAC
	//	_, err := jwt.Sign(jwt.HS256, "secret", claims) // Returns ErrInvalidKey
	//
	//	// Correct: []byte for HMAC
	//	_, err = jwt.Sign(jwt.HS256, []byte("secret"), claims)
	//
	//	// Wrong: RSA key for HMAC algorithm
	//	_, err = jwt.Sign(jwt.HS256, rsaPrivateKey, claims) // Returns ErrInvalidKey
	//
	//	// Correct: RSA key for RSA algorithm
	//	_, err = jwt.Sign(jwt.RS256, rsaPrivateKey, claims)
	ErrInvalidKey = errors.New("jwt: invalid key")
)
View Source
var (
	// ErrExpired indicates that a JWT token has passed its expiration time.
	// This error occurs when the current time (determined by the Clock function) is after
	// the time specified in the "exp" (expiry) claim. This is a critical security validation
	// that prevents the use of tokens beyond their intended lifetime.
	//
	// Common scenarios:
	//   - Token has naturally expired after its MaxAge duration
	//   - Clock skew between token issuer and verifier
	//   - Long-running operations that outlast token validity
	//   - Cached tokens that weren't refreshed in time
	//
	// Handling strategies:
	//   - Refresh the token if refresh tokens are available
	//   - Re-authenticate the user
	//   - Return 401 Unauthorized to the client
	//   - Implement token auto-renewal mechanisms
	//
	// Note: Some applications implement leeway (time tolerance) to handle
	// minor clock differences between systems. Use appropriate validators for this.
	ErrExpired = errors.New("jwt: token expired")

	// ErrNotValidYet indicates that a JWT token is being used before its valid time period.
	// This error occurs when the current time is before the time specified in the "nbf"
	// (not before) claim. This prevents premature use of tokens that are intended for
	// future activation.
	//
	// Common use cases for "nbf" claim:
	//   - Scheduled token activation for batch operations
	//   - Pre-issued tokens for future events or sessions
	//   - Time-delayed access grants
	//   - Coordinated multi-system token activation
	//
	// This validation ensures that tokens cannot be used until their intended
	// activation time, providing temporal access control capabilities.
	//
	// Example scenarios:
	//   - Tokens issued now but valid from midnight
	//   - Conference tickets valid only during event dates
	//   - Subscription tokens that activate on payment confirmation
	ErrNotValidYet = errors.New("jwt: token not valid yet")

	// ErrIssuedInTheFuture indicates that a JWT token has an "iat" (issued at) claim
	// set to a future time. This error occurs when the token's issue time is after
	// the current time, which suggests either clock skew or potentially malicious
	// token manipulation.
	//
	// This validation prevents acceptance of tokens that claim to be issued in the future,
	// which could indicate:
	//   - Significant clock synchronization issues between systems
	//   - Attempted token forgery with manipulated timestamps
	//   - Misconfigured token generation systems
	//   - Time zone handling errors in token issuance
	//
	// Security implications:
	//   - Helps detect potentially tampered tokens
	//   - Ensures logical consistency of token timestamps
	//   - Prevents replay attacks using future-dated tokens
	//
	// Consider implementing reasonable time leeway (e.g., 5 minutes) to handle
	// minor clock differences in distributed systems while maintaining security.
	ErrIssuedInTheFuture = errors.New("jwt: token issued in the future")
)
View Source
var (
	// ErrEmptyKid indicates that a JWT header is missing the required "kid" (Key ID) field.
	// This error occurs when using key-based validation but the token doesn't specify which key to use.
	ErrEmptyKid = errors.New("jwt: kid is empty")

	// ErrUnknownKid indicates that a JWT header contains a "kid" field that doesn't match
	// any registered key. This typically means the token was signed with a key not known to this application.
	ErrUnknownKid = errors.New("jwt: unknown kid")
)
View Source
var (
	// ErrMissing indicates that a JWT token is empty or nil when passed to verification functions.
	// This error occurs when attempting to verify a token that has no content, preventing
	// any meaningful validation or parsing operations.
	//
	// Common scenarios that trigger this error:
	//   - Passing nil or empty byte slice to Verify functions
	//   - Missing Authorization header in HTTP requests
	//   - Empty token strings after extraction from requests
	//
	// Example:
	//   err := jwt.Verify(jwt.HS256, hmacKey, nil, &claims)
	//   // Returns: ErrMissing
	ErrMissing = errors.New("jwt: token is empty")

	// ErrTokenForm indicates that a JWT token does not have the expected three-part structure.
	// Valid JWT tokens must follow the format: header.payload.signature (exactly three parts
	// separated by dots). This error occurs during token parsing when the structure is malformed.
	//
	// Common causes:
	//   - Truncated tokens missing parts (e.g., "header.payload")
	//   - Extra dots creating more than three parts
	//   - Completely malformed token strings
	//   - Binary data passed instead of base64-encoded JWT
	//
	// Example of invalid tokens:
	//   "header.payload"           // Missing signature
	//   "header.payload.sig.extra" // Too many parts
	//   "invalid-token-format"     // Wrong structure
	ErrTokenForm = errors.New("jwt: invalid token form")

	// ErrTokenAlg indicates an algorithm mismatch between the expected algorithm and
	// the algorithm specified in the JWT header. This is a critical security validation
	// that prevents algorithm substitution attacks.
	//
	// This error occurs when:
	//   - Token header "alg" field doesn't match the verification algorithm
	//   - Header structure is malformed or unexpected
	//   - Algorithm field ordering differs from standard format
	//   - Header contains unsupported or unknown algorithm names
	//
	// Security note: This validation is essential to prevent attacks where
	// malicious tokens specify weaker algorithms than expected.
	//
	// Example:
	//   // Token header: {"alg":"HS256","typ":"JWT"}
	//   // But verifying with: jwt.Verify(jwt.RS256, ...)
	//   // Returns: ErrTokenAlg
	ErrTokenAlg = errors.New("jwt: unexpected token algorithm")
)
View Source
var Clock = time.Now

Clock provides the current time for JWT expiration and time-based claim validation.

This variable is used throughout the library for all time-related validations including:

  • Token expiration (exp claim) verification
  • Not-before (nbf claim) validation
  • Issued-at (iat claim) verification
  • Leeway calculations for time-based validators

The default implementation uses time.Now(), but it can be overridden for:

  • **Testing**: Set fixed times to create deterministic test scenarios
  • **Time zones**: Use custom time sources for specific timezone requirements
  • **Simulation**: Simulate future or past times for testing edge cases
  • **Synchronized time**: Use network time protocols for distributed systems

**Thread Safety**: Modifications to Clock should be done during application initialization before concurrent operations begin. The function itself should be thread-safe as it may be called from multiple goroutines simultaneously.

Example usage:

// Default usage (production)
now := jwt.Clock() // Returns current time

// Testing with fixed time
fixedTime := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
jwt.Clock = func() time.Time { return fixedTime }

// Testing with relative time
startTime := time.Now()
jwt.Clock = func() time.Time { return startTime.Add(2 * time.Hour) }

// Reset to default
jwt.Clock = time.Now

When testing time-sensitive functionality, remember to restore the original Clock function after tests complete to avoid affecting other tests.

View Source
var ErrBlocked = errors.New("jwt: token is blocked")

ErrBlocked indicates that the token has not yet expired but was blocked by the server's Blocklist.

View Source
var ErrDecrypt = errors.New("jwt: decrypt: payload authentication failed")

ErrDecrypt indicates a failure during payload decryption. This error is returned when GCM authentication fails, which could indicate tampering, corruption, or use of the wrong decryption key.

View Source
var ErrExpected = errors.New("jwt: field not match")

ErrExpected indicates that a standard claim did not match the expected value. Use errors.Is() to check for this specific validation failure.

Example:

verifiedToken, err := jwt.Verify(alg, key, token, expected)
if errors.Is(err, jwt.ErrExpected) {
    // Handle validation failure
    log.Printf("Claim validation failed: %v", err)
}
View Source
var ErrMissingKey = errors.New("jwt: token is missing a required field")

ErrMissingKey indicates that a token is missing a required JSON field. This error is returned when using UnmarshalWithRequired and a struct field tagged with `json:"field,required"` is missing from the token payload.

Use errors.Is(err, ErrMissingKey) to check for this specific error.

View Source
var Marshal = func(v any) ([]byte, error) {
	if b, ok := v.([]byte); ok {
		return b, nil
	}

	return json.Marshal(v)
}

Marshal defines the JSON marshaling function used for encoding JWT payloads.

This package-level variable allows customization of how claims are serialized to JSON before being included in JWT tokens. The default implementation handles both raw byte slices and arbitrary Go values using the standard json.Marshal.

**Default Behavior**:

  • Raw []byte values are passed through unchanged (for pre-serialized JSON)
  • All other values are marshaled using standard json.Marshal
  • Supports any type that implements json.Marshaler interface

**Customization Use Cases**:

  • **Custom JSON encoding**: Use alternative JSON libraries (e.g., jsoniter, gojay)
  • **Field transformation**: Apply custom field naming, omission, or formatting
  • **Compression**: Compress payloads before encoding for large claims
  • **Encryption**: Encrypt sensitive fields before marshaling
  • **Validation**: Add payload validation during marshaling
  • **Debugging**: Add logging or monitoring to track payload sizes

**Performance Considerations**: The Marshal function is called for every token generation, so performance optimizations here can significantly impact throughput.

**Thread Safety**: This variable should be set during application initialization. The function itself must be thread-safe as it's called from concurrent operations.

Example customizations:

// Use jsoniter for better performance
import jsoniter "github.com/json-iterator/go"
jwt.Marshal = jsoniter.Marshal

// Add payload size logging
originalMarshal := jwt.Marshal
jwt.Marshal = func(v any) ([]byte, error) {
    data, err := originalMarshal(v)
    if err == nil {
        log.Printf("JWT payload size: %d bytes", len(data))
    }
    return data, err
}

// Custom field formatting
jwt.Marshal = func(v any) ([]byte, error) {
    if b, ok := v.([]byte); ok {
        return b, nil
    }

    // Apply custom transformations
    transformed := applyCustomFormatting(v)
    return json.Marshal(transformed)
}
View Source
var Plain = TokenValidatorFunc(func(token []byte, standardClaims Claims, err error) error {
	if err == errPayloadNotJSON {
		return nil
	}

	return err
})

Plain is a TokenValidator that allows verification of tokens with non-JSON payloads.

This validator enables successful verification of JWT tokens that contain plain text or other non-JSON data in their payload. It works by catching and ignoring the errPayloadNotJSON error that would normally cause verification to fail.

**Use Cases**:

  • Legacy tokens with plain text payloads
  • Simple tokens without structured claims
  • Custom token formats that don't use JSON
  • Debugging and testing with malformed tokens
  • Integration with non-standard JWT implementations

**Behavior**: When errPayloadNotJSON occurs during verification, this validator returns nil (success) instead of the error, allowing the verification process to continue. All other errors are passed through unchanged.

**Payload Access**: With Plain validator, the raw payload bytes can be accessed via VerifiedToken.Payload field, but the Claims() method will likely fail since the payload is not JSON.

**Security Considerations**:

  • Standard claims validation (exp, nbf, iat) is bypassed for non-JSON payloads
  • Application must implement custom validation for plain payloads
  • Signature verification still occurs normally
  • Use only when non-JSON payloads are expected and acceptable

Example usage:

// Verify token that may have plain text payload
verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, tokenBytes, jwt.Plain)
if err != nil {
    log.Printf("Verification failed: %v", err)
    return
}

// Access raw payload (likely plain text)
payloadText := string(verifiedToken.Payload)
log.Printf("Plain payload: %s", payloadText)

// Standard claims will be empty for non-JSON payloads
log.Printf("Subject: %s", verifiedToken.StandardClaims.Subject) // Will be empty

// Custom payload validation
if !isValidPlainPayload(payloadText) {
    return errors.New("invalid plain payload content")
}

// Combined with other validators (Plain should come last)
verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, tokenBytes,
    customHeaderValidator,
    jwt.Plain) // Plain overrides JSON errors

**Validator Order**: When using Plain with other validators, place it last in the validator list so it can catch JSON errors that other validators might depend on.

View Source
var ReadFile = os.ReadFile

ReadFile defines the file reading function used by key loading utilities.

This package-level variable allows customization of how the library reads key files when using helper functions like MustLoadRSA, LoadRSA, and similar key loading utilities. The default implementation uses os.ReadFile for standard filesystem access.

**Use Cases for Customization**:

  • **Embedded files**: Read keys from embedded filesystem (embed.FS)
  • **Remote storage**: Fetch keys from cloud storage, databases, or APIs
  • **Encrypted storage**: Read and decrypt keys from secure storage
  • **Virtual filesystems**: Support non-standard file sources
  • **Testing**: Use in-memory file systems for unit tests

**Function Signature**: Must match `func(filename string) ([]byte, error)` to be compatible with standard os.ReadFile behavior.

**Thread Safety**: This variable should be set during application initialization before concurrent key loading operations. The function itself should be thread-safe as it may be called from multiple goroutines.

Example customizations:

// Embedded filesystem (Go 1.16+)
//go:embed keys/*
var keyFiles embed.FS
jwt.ReadFile = keyFiles.ReadFile

// Remote key storage
jwt.ReadFile = func(filename string) ([]byte, error) {
    resp, err := http.Get("https://keyserver.com/keys/" + filename)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    return io.ReadAll(resp.Body)
}

// Database storage
jwt.ReadFile = func(filename string) ([]byte, error) {
    var keyData []byte
    err := db.QueryRow("SELECT key_data FROM keys WHERE filename = ?", filename).Scan(&keyData)
    return keyData, err
}

// Testing with in-memory files
testFiles := map[string][]byte{
    "test.pem": []byte("-----BEGIN PRIVATE KEY-----\n..."),
}
jwt.ReadFile = func(filename string) ([]byte, error) {
    if data, ok := testFiles[filename]; ok {
        return data, nil
    }
    return nil, os.ErrNotExist
}
View Source
var Unmarshal = defaultUnmarshal

Unmarshal defines the JSON unmarshaling function used for decoding JWT payloads.

This package-level variable allows customization of how JWT payload JSON is deserialized into Go data structures. The default implementation uses json.Decoder with UseNumber() enabled to preserve numeric precision and avoid float64 conversion issues common with JSON parsing.

**Default Behavior (defaultUnmarshal)**:

  • Uses json.Decoder with UseNumber() to handle integers correctly
  • Prevents automatic conversion of all numbers to float64
  • Preserves numeric precision for large integers
  • Decodes JSON numbers as json.Number type when destination is any

**Why UseNumber() Matters**:

  • Standard json.Unmarshal converts all numbers to float64
  • Float64 cannot accurately represent large integers (>53 bits)
  • JWT claims like "iat", "exp", "nbf" are Unix timestamps (large integers)
  • User IDs and other identifiers may be large integers

**Customization Use Cases**:

  • **Alternative JSON libraries**: Use faster or feature-rich JSON libraries
  • **Custom number handling**: Different numeric type preferences
  • **Field validation**: Add validation during unmarshaling
  • **Field transformation**: Apply custom transformations to incoming data
  • **Debugging**: Add logging to monitor claim structures
  • **Security**: Add input sanitization or filtering

**Thread Safety**: This variable should be set during application initialization. The function itself must be thread-safe as it's called from concurrent operations.

Example customizations:

// Use jsoniter for better performance
import jsoniter "github.com/json-iterator/go"
jwt.Unmarshal = jsoniter.Unmarshal

// Add claim logging for debugging
originalUnmarshal := jwt.Unmarshal
jwt.Unmarshal = func(data []byte, v any) error {
    log.Printf("Unmarshaling JWT claims: %s", string(data))
    return originalUnmarshal(data, v)
}

// Custom number handling without UseNumber()
jwt.Unmarshal = func(data []byte, v any) error {
    return json.Unmarshal(data, v) // Standard behavior
}

// Add validation during unmarshaling
jwt.Unmarshal = func(data []byte, v any) error {
    if err := defaultUnmarshal(data, v); err != nil {
        return err
    }
    return validateClaimsStructure(v) // Custom validation
}

Functions

func Base64Decode

func Base64Decode(src []byte) ([]byte, error)

Base64Decode decodes JWT-specific base64url format bytes to raw data.

JWT tokens use base64url encoding (RFC 4648 Section 5) without trailing padding characters. This function handles the JWT-specific decoding by automatically adding required padding before standard base64 decoding.

The decoding process:

  • Calculates missing padding length based on input length
  • Adds appropriate number of '=' padding characters
  • Performs standard base64.URLEncoding decode operation
  • Returns the raw decoded bytes

This function is used internally for decoding JWT headers, payloads, and signatures. It correctly handles the padding-free format used in JWT tokens.

Parameters:

  • src: Base64url-encoded bytes (without padding)

Returns:

  • []byte: Decoded raw bytes
  • error: Decoding error if the input is invalid base64url

The function automatically handles inputs with or without padding, making it compatible with both JWT format and standard base64url.

Example:

encoded := []byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9")
decoded, err := jwt.Base64Decode(encoded)
if err != nil {
    log.Fatal(err)
}
// Result: []byte(`{"alg":"HS256","typ":"JWT"}`)

func Base64Encode

func Base64Encode(src []byte) []byte

Base64Encode encodes bytes to JWT-specific base64url format without padding.

JWT tokens use base64url encoding (RFC 4648 Section 5) without trailing padding characters ('='). This function provides optimized encoding that follows JWT specifications exactly.

The encoding process:

  • Uses standard base64.URLEncoding for initial encoding
  • Removes trailing '=' padding characters as required by JWT spec
  • Returns URL-safe base64 encoded bytes

This function is used internally for encoding JWT headers, payloads, and signatures. While optimized for internal use, it can be used externally for compatible base64url encoding needs.

Parameters:

  • src: Raw bytes to encode

Returns:

  • []byte: Base64url-encoded bytes without padding

Example:

data := []byte(`{"alg":"HS256","typ":"JWT"}`)
encoded := jwt.Base64Encode(data)
// Result: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
// Note: No trailing '=' characters

func BytesQuote added in v0.0.4

func BytesQuote(b []byte) []byte

BytesQuote wraps a byte slice in double quotes to create a JSON string value.

This function creates a new byte slice with the input data surrounded by double quotes, making it suitable for use as a JSON string value.

The function allocates a new slice that is exactly len(b)+2 bytes long and copies the input data between the quotes.

Example:

token := []byte("eyJhbGciOiJIUzI1NiJ9...")
quoted := jwt.BytesQuote(token)
// quoted = []byte("\"eyJhbGciOiJIUzI1NiJ9...\"")

This is primarily used internally by NewTokenPair but can be useful for other JSON formatting scenarios.

func BytesToString

func BytesToString(b []byte) string

BytesToString converts a byte slice to a string without memory allocation.

This function uses unsafe operations to avoid copying the underlying byte data, providing better performance for frequent conversions. The resulting string shares the same underlying memory as the input byte slice.

WARNING: This is unsafe because:

  • If the byte slice is modified after conversion, the string changes too
  • The string will be invalid if the byte slice is garbage collected
  • Only use when you control the lifecycle of both the bytes and string

This implementation requires Go 1.20+ for unsafe.String and unsafe.SliceData. For safer builds, use the "safe" build tag which provides a standard conversion.

Example:

data := []byte("hello")
str := jwt.BytesToString(data) // No allocation
// Do NOT modify data after this point

func EncodePrivateKeyToPEM added in v0.1.15

func EncodePrivateKeyToPEM(key PrivateKey) (string, error)

EncodePrivateKeyToPEM converts a private key to PEM-encoded string format.

This function supports encoding of private keys for the following algorithms:

  • RSA: Encodes as PKCS#1 format ("RSA PRIVATE KEY")
  • ECDSA: Encodes as SEC1 format ("EC PRIVATE KEY")
  • Ed25519: Encodes as PKCS#8 format ("PRIVATE KEY")

The resulting PEM string can be saved to files, stored in configuration, or transmitted securely. It's the inverse operation of the Parse* functions.

Returns an error if the key type is not supported for PEM encoding.

Example:

privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
pemString, err := jwt.EncodePrivateKeyToPEM(privateKey)
if err != nil {
    log.Fatal(err)
}

// Save to file
err = os.WriteFile("private.pem", []byte(pemString), 0600)

func EncodePublicKeyToPEM added in v0.1.15

func EncodePublicKeyToPEM(key PublicKey) (string, error)

EncodePublicKeyToPEM converts a public key to PEM-encoded string format.

This function supports encoding of public keys for the following algorithms:

  • RSA: Encodes in PKIX format ("PUBLIC KEY")
  • ECDSA: Encodes in PKIX format ("PUBLIC KEY")
  • Ed25519: Encodes in PKIX format ("PUBLIC KEY")

The resulting PEM string is safe to share publicly and can be distributed for token verification. It's commonly used for JWKS generation and configuration file storage.

Returns an error if the key type is not supported for PEM encoding.

Example:

publicKey := &privateKey.PublicKey // From RSA private key
pemString, err := jwt.EncodePublicKeyToPEM(publicKey)
if err != nil {
    log.Fatal(err)
}

// Include in JWKS or configuration
config := KeyConfiguration{
    Public: pemString,
    // ... other fields
}

func Enrich added in v0.1.16

func Enrich(key PrivateKey, accessToken []byte, extraClaims any) ([]byte, error)

Enrich creates a new JWT token by merging claims from an existing token with additional claims.

This function takes an existing JWT token, extracts its claims, merges them with the provided extra claims, and creates a new properly signed token using the same algorithm as the original token. This is useful for scenarios where external systems need to add role-based fields or other metadata to existing tokens while preserving the original claims.

Important: It's not possible to modify just the payload of a JWT without changing the signature, since the signature is calculated over the entire header.payload content. This function creates a completely new token with a new signature.

Parameters:

  • key: Key for both verifying the original token and signing the new token
  • accessToken: Existing JWT token to extract claims from
  • extraClaims: Additional claims to merge with existing claims

Algorithm Detection: The algorithm is automatically extracted from the original token's header, ensuring the enriched token uses the same algorithm as the original.

Claim Merging Behavior:

  • Original token claims are preserved
  • Extra claims are merged without extra validation
  • Uses the same merging logic as the Sign function

Security Considerations:

  • The original token should be verified by the caller before claim extraction
  • The new token is properly signed with the same algorithm and provided key
  • Consider the security implications of merging untrusted extra claims
  • Validate extra claims before passing them to this function
  • Encrypted tokens are not supported

Use Cases:

  • Adding role-based permissions to existing user tokens
  • Enriching tokens with organization-specific data
  • Token transformation in microservice architectures
  • Adding audit trails or metadata to existing tokens

Example usage:

// Basic enrichment with role information
extraClaims := map[string]any{
    "role":        "admin",
    "permissions": []string{"read", "write", "delete"},
    "department":  "engineering",
}

enrichedToken, err := jwt.Enrich(signingKey, existingToken, extraClaims)
if err != nil {
    log.Printf("Token enrichment failed: %v", err)
    return
}

// Enrichment with additional standard claims
organizationInfo := jwt.Map{
    "org_id":   "org123",
    "org_name": "MyCompany",
    "tenant":   "tenant456",
}

enrichedToken, err := jwt.Enrich(signingKey, userToken,
    organizationInfo,
    jwt.MaxAge(2 * time.Hour),        // Extend expiration
    jwt.Audience{"enriched-api"})     // Add new audience

// Role-based enrichment
roleData := struct {
    Role        string   `json:"role"`
    Permissions []string `json:"permissions"`
    Level       int      `json:"access_level"`
}{
    Role:        "manager",
    Permissions: []string{"user_management", "reporting"},
    Level:       5,
}

enrichedToken, err := jwt.Enrich(signingKey, originalToken, roleData)

Error Conditions:

  • Unsupported or invalid algorithm in original token
  • Claim extraction or merging failures
  • New token signing failures
  • Invalid extra claims (non-JSON serializable)

Performance Notes:

  • It does not verify the original token
  • Performs JSON marshaling and unmarshaling operations
  • Creates a completely new token with new signature
  • Consider caching enriched tokens if appropriate for your use case

func GenerateBase64EdDSA added in v0.0.14

func GenerateBase64EdDSA() (string, string, error)

GenerateBase64EdDSA generates a random Ed25519 key pair as base64-encoded strings.

This function generates a new Ed25519 key pair and returns the keys as base64-encoded strings using raw standard encoding (no padding). This format is convenient for configuration files or environment variables.

Returns the public key string, private key string, and any error that occurred.

Example:

publicKey, privateKey, err := jwt.GenerateBase64EdDSA()
if err != nil {
    log.Fatal(err)
}
// Use keys directly or store in config
fmt.Printf("Public Key: %s\n", publicKey)
fmt.Printf("Private Key: %s\n", privateKey)

func GenerateEdDSA added in v0.0.14

func GenerateEdDSA() (ed25519.PublicKey, ed25519.PrivateKey, error)

GenerateEdDSA generates a random Ed25519 key pair and returns them as PEM-encoded data.

This function generates a new Ed25519 key pair and encodes both keys in PEM format (PKCS#8 for private key, PKIX for public key).

Returns the public key PEM, private key PEM, and any error that occurred. Note: The returned order is (publicPEM, privatePEM), which differs from the conventional (private, public) order.

Example:

publicPEM, privatePEM, err := jwt.GenerateEdDSA()
if err != nil {
    log.Fatal(err)
}
// Save to files or use directly

func HasRequiredJSONTag added in v0.0.4

func HasRequiredJSONTag(field reflect.StructField) bool

HasRequiredJSONTag reports whether a struct field has the "required" JSON tag.

This function checks if a struct field is marked as required using the `json:"fieldname,required"` tag syntax. It only considers exported fields (fields with uppercase first letter).

This function is useful for:

  • Pre-validation of struct definitions
  • Building custom unmarshaling logic
  • Debugging required field configurations

Example:

type Claims struct {
    Username string `json:"username,required"`
    Email    string `json:"email"`
}

field, _ := reflect.TypeOf(Claims{}).FieldByName("Username")
isRequired := jwt.HasRequiredJSONTag(field) // returns true

func LoadHMAC

func LoadHMAC(filenameOrRaw string) ([]byte, error)

LoadHMAC loads an HMAC key from a file or treats the input as raw key data.

If the input is a valid file path, it reads the file contents as the key. Otherwise, it treats the input string as raw key data and converts it to []byte. This provides flexibility for both file-based and inline key configuration.

Pass the returned value to both Sign and Verify functions.

Returns an error if the file exists but cannot be read.

Example:

// Load from file
key, err := jwt.LoadHMAC("secret.key")
if err != nil {
    log.Fatal(err)
}

// Use raw string (never fails)
key, _ := jwt.LoadHMAC("my-secret-key-here")

token, err := jwt.Sign(jwt.HS256, key, claims)

func LoadPrivateKeyECDSA

func LoadPrivateKeyECDSA(filename string) (*ecdsa.PrivateKey, error)

LoadPrivateKeyECDSA loads and parses a PEM-encoded ECDSA private key from a file.

The file should contain a PEM-encoded ECDSA private key in PKCS#1 or PKCS#8 format. Pass the returned value to Sign functions for token creation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

privateKey, err := jwt.LoadPrivateKeyECDSA("ecdsa_private_key.pem")
if err != nil {
    log.Fatal(err)
}
token, err := jwt.Sign(jwt.ES256, privateKey, claims)

func LoadPrivateKeyEdDSA

func LoadPrivateKeyEdDSA(filename string) (ed25519.PrivateKey, error)

LoadPrivateKeyEdDSA loads and parses a PEM-encoded Ed25519 private key from a file.

The file should contain a PEM-encoded Ed25519 private key in PKCS#8 format. Pass the returned value to Sign functions for token creation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

privateKey, err := jwt.LoadPrivateKeyEdDSA("ed25519_private_key.pem")
if err != nil {
    log.Fatal(err)
}
token, err := jwt.Sign(jwt.EdDSA, privateKey, claims)

func LoadPrivateKeyRSA

func LoadPrivateKeyRSA(filename string) (*rsa.PrivateKey, error)

LoadPrivateKeyRSA loads and parses a PEM-encoded RSA private key from a file.

The file should contain a PEM-encoded RSA private key in PKCS#1 or PKCS#8 format. Pass the returned value to Sign functions for token creation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

privateKey, err := jwt.LoadPrivateKeyRSA("rsa_private_key.pem")
if err != nil {
    log.Fatal(err)
}
token, err := jwt.Sign(jwt.RS256, privateKey, claims)

func LoadPublicKeyECDSA

func LoadPublicKeyECDSA(filename string) (*ecdsa.PublicKey, error)

LoadPublicKeyECDSA loads and parses a PEM-encoded ECDSA public key from a file.

The file should contain a PEM-encoded ECDSA public key in PKIX format, or a certificate containing an ECDSA public key. Pass the returned value to Verify functions for token validation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

publicKey, err := jwt.LoadPublicKeyECDSA("ecdsa_public_key.pem")
if err != nil {
    log.Fatal(err)
}
verifiedToken, err := jwt.Verify(jwt.ES256, publicKey, token)

func LoadPublicKeyEdDSA

func LoadPublicKeyEdDSA(filename string) (ed25519.PublicKey, error)

LoadPublicKeyEdDSA loads and parses a PEM-encoded Ed25519 public key from a file.

The file should contain a PEM-encoded Ed25519 public key in PKIX format. Pass the returned value to Verify functions for token validation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

publicKey, err := jwt.LoadPublicKeyEdDSA("ed25519_public_key.pem")
if err != nil {
    log.Fatal(err)
}
verifiedToken, err := jwt.Verify(jwt.EdDSA, publicKey, token)

func LoadPublicKeyRSA

func LoadPublicKeyRSA(filename string) (*rsa.PublicKey, error)

LoadPublicKeyRSA loads and parses a PEM-encoded RSA public key from a file.

The file should contain a PEM-encoded RSA public key in PKIX format, or a certificate containing an RSA public key. Pass the returned value to Verify functions for token validation.

Returns an error if the file cannot be read or the key cannot be parsed.

Example:

publicKey, err := jwt.LoadPublicKeyRSA("rsa_public_key.pem")
if err != nil {
    log.Fatal(err)
}
verifiedToken, err := jwt.Verify(jwt.RS256, publicKey, token)

func MaxAgeMap

func MaxAgeMap(maxAge time.Duration, claims Map)

MaxAgeMap sets expiration and issued-at claims directly in a map-based claims structure.

This helper function provides expiration functionality for map-based claims (Map type) by directly setting the "exp" and "iat" fields. It's designed for use with custom claim structures that don't embed the standard Claims struct.

**Parameters**:

  • maxAge: Duration the token should remain valid from current time
  • claims: Map containing JWT claims (modified in-place)

**Behavior**:

  • If claims is nil: Function returns immediately (no-op)
  • If maxAge <= 1 second: Function returns immediately (no expiration set)
  • If "exp" already exists: Preserves existing expiration (no overwrite)
  • Otherwise: Sets both "exp" and "iat" to calculated values

**Claims Set**:

  • "exp": Current time + maxAge duration (Unix timestamp)
  • "iat": Current time (Unix timestamp)

**Time Source**: Uses the configurable Clock() function for current time, ensuring consistency with other JWT timing operations.

**Use Cases**:

  • Custom claim structures using map[string]any
  • Dynamic claim building without predefined structs
  • Legacy codebases using map-based claims
  • Flexible claim composition patterns

**Preservation Logic**: The function checks if "exp" is already set to avoid overwriting existing expiration times, allowing for selective application.

Example usage:

// Create custom claims with expiration
claims := jwt.Map{
    "user_id": "12345",
    "role":    "admin",
    "scope":   []string{"read", "write"},
}

// Add 15-minute expiration
jwt.MaxAgeMap(15 * time.Minute, claims)
token, err := jwt.Sign(jwt.HS256, secretKey, claims)

// Respects existing expiration
claimsWithExpiry := jwt.Map{
    "user_id": "12345",
    "exp":     time.Now().Add(time.Hour).Unix(), // Preserved
}
jwt.MaxAgeMap(15 * time.Minute, claimsWithExpiry) // No change to exp

// Use before signing
claims := jwt.Map{"foo": "bar"}
jwt.MaxAgeMap(15 * time.Minute, claims)
jwt.Sign(alg, key, claims)

func Merge

func Merge(values ...any) ([]byte, error)

Merge combines multiple values into a single JSON object for JWT claims.

This utility function merges a variadic number of values into a unified JSON object, enabling flexible composition of JWT claims from multiple sources. It's used internally by the Sign function and can be used directly for custom claim composition scenarios.

**Input Requirements**:

  • Each non-nil value must marshal to a valid JSON object
  • Objects must start with '{' and end with '}'
  • Nil values are safely ignored
  • Empty objects ("{}") are skipped during merging

**Supported Input Types**:

  • Structs with JSON tags (Claims, custom claim structs)
  • map[string]any and similar map types
  • []byte containing valid JSON object
  • string containing valid JSON object
  • Any type implementing json.Marshaler for objects

**Merging Logic**:

  • Values are processed in order (left to right)
  • Later values can override earlier values for same keys
  • Object contents are merged at the top level
  • Returns combined JSON as []byte

**Error Conditions**:

  • Returns error if any value fails to marshal
  • Returns error if marshaled result is not a JSON object
  • Includes position information in error messages

**Automatic Usage**: This function is automatically called by Sign when multiple SignOption values are provided, enabling seamless claim composition.

Example usage:

// Merge standard and custom claims
standardClaims := jwt.Claims{
    Issuer:  "myapp.com",
    Subject: "user123",
    Expiry:  time.Now().Add(time.Hour).Unix(),
}

customClaims := map[string]any{
    "role":        "admin",
    "permissions": []string{"read", "write", "delete"},
    "department":  "engineering",
}

// Direct merge usage
combined, err := jwt.Merge(standardClaims, customClaims)
if err != nil {
    log.Fatal(err)
}
token, err := jwt.Sign(jwt.HS256, secretKey, combined)

// Automatic merge in Sign function
token, err := jwt.Sign(jwt.HS256, secretKey, customClaims,
    jwt.MaxAge(15 * time.Minute),  // Adds exp, iat
    jwt.Claims{Issuer: "myapp.com"}) // Adds iss

// Multiple map merging
userInfo := map[string]any{"user_id": "123", "email": "user@example.com"}
permissions := map[string]any{"role": "user", "scope": "read"}
metadata := map[string]any{"version": "1.0", "client": "mobile"}

allClaims, err := jwt.Merge(userInfo, permissions, metadata)

**Note**: When the same key exists in multiple objects, the last occurrence takes precedence, allowing for override patterns in claim composition.

func MustGenerateRandom

func MustGenerateRandom(n int) []byte

MustGenerateRandom generates a cryptographically secure random byte slice of length n. This is suitable for creating HMAC keys.

For HMAC algorithms, recommended key sizes are: - HS256: 32 bytes - HS384: 48 bytes - HS512: 64 bytes

This function panics if random generation fails. Use crypto/rand.Read directly for error handling.

Example:

key := jwt.MustGenerateRandom(32) // For HS256
token, err := jwt.Sign(jwt.HS256, key, claims)

func MustGenerateRandomBase64 added in v0.0.14

func MustGenerateRandomBase64(length int, padding rune) string

MustGenerateRandomBase64 generates a cryptographically secure random base64-encoded string.

The length parameter specifies the number of random bytes to generate before encoding. The padding parameter controls base64 padding ('=' characters). Use jwt.NoPadding or base64.NoPadding to disable padding.

This function panics if random generation fails.

Example:

// Generate 32 random bytes, base64-encoded without padding
randomB64 := jwt.MustGenerateRandomBase64(32, jwt.NoPadding)

// Generate 24 random bytes, base64-encoded with standard padding
randomB64 := jwt.MustGenerateRandomBase64(24, base64.StdPadding)

func MustGenerateRandomString added in v0.0.14

func MustGenerateRandomString(length int) string

MustGenerateRandomString generates a cryptographically secure random string of the specified length. The string contains only letters (a-z, A-Z).

This function uses a secure random number generator and efficient bit manipulation to generate random strings suitable for keys, tokens, or identifiers.

This function panics if random generation fails.

Example:

randomStr := jwt.MustGenerateRandomString(32)
fmt.Println(randomStr) // Output: "aBcDeFgHiJkLmNoPqRsTuVwXyZ..."

func MustLoadECDSA

func MustLoadECDSA(privateKeyFilename, publicKeyFilename string) (*ecdsa.PrivateKey, *ecdsa.PublicKey)

MustLoadECDSA accepts private and public PEM filenames and returns a pair of private and public ECDSA keys.

Pass the returned private key to the Sign functions and the public key to the Verify functions.

This function panics if either key file cannot be read or parsed. Use LoadPrivateKeyECDSA and LoadPublicKeyECDSA for error handling.

Example:

privateKey, publicKey := jwt.MustLoadECDSA("private.pem", "public.pem")
token, err := jwt.Sign(jwt.ES256, privateKey, claims)
verifiedToken, err := jwt.Verify(jwt.ES256, publicKey, token)

func MustLoadEdDSA

func MustLoadEdDSA(privateKeyFilename, publicKeyFilename string) (ed25519.PrivateKey, ed25519.PublicKey)

MustLoadEdDSA accepts private and public PEM filenames and returns a pair of private and public Ed25519 keys.

Pass the returned private key to Sign functions and the public key to Verify functions.

This function panics if either key file cannot be read or parsed. Use LoadPrivateKeyEdDSA and LoadPublicKeyEdDSA for error handling.

Example:

privateKey, publicKey := jwt.MustLoadEdDSA("ed25519_private.pem", "ed25519_public.pem")
token, err := jwt.Sign(jwt.EdDSA, privateKey, claims)
verifiedToken, err := jwt.Verify(jwt.EdDSA, publicKey, token)

func MustLoadHMAC

func MustLoadHMAC(filenameOrRaw string) []byte

MustLoadHMAC loads an HMAC key from a file or treats the input as raw key data.

If the input is a valid file path, it reads the file contents as the key. Otherwise, it treats the input string as raw key data and converts it to []byte. This provides flexibility for both file-based and inline key configuration.

Pass the returned value to both Sign and Verify functions.

This function panics if the file cannot be read. Use LoadHMAC for error handling.

Example:

// Load from file
key := jwt.MustLoadHMAC("secret.key")

// Use raw string
key := jwt.MustLoadHMAC("my-secret-key-here")

token, err := jwt.Sign(jwt.HS256, key, claims)

func MustLoadRSA

func MustLoadRSA(privateKeyFilename, publicKeyFilename string) (*rsa.PrivateKey, *rsa.PublicKey)

MustLoadRSA accepts private and public PEM file paths and returns a pair of private and public RSA keys.

Pass the returned private key to Sign functions and the public key to Verify functions.

This function panics if either key file cannot be read or parsed. Use LoadPrivateKeyRSA and LoadPublicKeyRSA for error handling.

Example:

privateKey, publicKey := jwt.MustLoadRSA("rsa_private.pem", "rsa_public.pem")
token, err := jwt.Sign(jwt.RS256, privateKey, claims)
verifiedToken, err := jwt.Verify(jwt.RS256, publicKey, token)

func ParsePrivateKeyECDSA

func ParsePrivateKeyECDSA(key []byte) (*ecdsa.PrivateKey, error)

ParsePrivateKeyECDSA decodes and parses PEM-encoded ECDSA private key bytes.

The input should be PEM-encoded ECDSA private key data. This function handles the low-level parsing after PEM decoding.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPrivateKeyECDSA for file-based loading.

Example:

keyData := []byte("-----BEGIN EC PRIVATE KEY-----\n...")
privateKey, err := jwt.ParsePrivateKeyECDSA(keyData)

func ParsePrivateKeyEdDSA

func ParsePrivateKeyEdDSA(key []byte) (ed25519.PrivateKey, error)

ParsePrivateKeyEdDSA decodes and parses PEM-encoded Ed25519 private key bytes.

The input should be PEM-encoded Ed25519 private key data in PKCS#8 format. This function handles the ASN.1 parsing to extract the seed and generate the full Ed25519 private key.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPrivateKeyEdDSA for file-based loading.

Example:

keyData := []byte("-----BEGIN PRIVATE KEY-----\n...")
privateKey, err := jwt.ParsePrivateKeyEdDSA(keyData)

func ParsePrivateKeyRSA

func ParsePrivateKeyRSA(key []byte) (*rsa.PrivateKey, error)

ParsePrivateKeyRSA decodes and parses PEM-encoded RSA private key bytes.

The input should be PEM-encoded RSA private key data in PKCS#1 or PKCS#8 format. This function handles both formats automatically.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPrivateKeyRSA for file-based loading.

Example:

keyData := []byte("-----BEGIN RSA PRIVATE KEY-----\n...")
privateKey, err := jwt.ParsePrivateKeyRSA(keyData)

func ParsePublicKeyECDSA

func ParsePublicKeyECDSA(key []byte) (*ecdsa.PublicKey, error)

ParsePublicKeyECDSA decodes and parses PEM-encoded ECDSA public key bytes.

The input should be PEM-encoded ECDSA public key data in PKIX format, or a certificate containing an ECDSA public key. This function handles the low-level parsing after PEM decoding.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPublicKeyECDSA for file-based loading.

Example:

keyData := []byte("-----BEGIN PUBLIC KEY-----\n...")
publicKey, err := jwt.ParsePublicKeyECDSA(keyData)

func ParsePublicKeyEdDSA

func ParsePublicKeyEdDSA(key []byte) (ed25519.PublicKey, error)

ParsePublicKeyEdDSA decodes and parses PEM-encoded Ed25519 public key bytes.

The input should be PEM-encoded Ed25519 public key data in PKIX format. This function handles the ASN.1 parsing to extract the public key bytes.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPublicKeyEdDSA for file-based loading.

Example:

keyData := []byte("-----BEGIN PUBLIC KEY-----\n...")
publicKey, err := jwt.ParsePublicKeyEdDSA(keyData)

func ParsePublicKeyRSA

func ParsePublicKeyRSA(key []byte) (*rsa.PublicKey, error)

ParsePublicKeyRSA decodes and parses PEM-encoded RSA public key bytes.

The input should be PEM-encoded RSA public key data in PKIX format, or a certificate containing an RSA public key. This function handles both formats automatically.

Returns an error if the PEM block is missing or the key cannot be parsed. Use LoadPublicKeyRSA for file-based loading.

Example:

keyData := []byte("-----BEGIN PUBLIC KEY-----\n...")
publicKey, err := jwt.ParsePublicKeyRSA(keyData)

func Sign

func Sign(alg Alg, key PrivateKey, claims any, opts ...SignOption) ([]byte, error)

Sign creates and signs a JWT token using the specified algorithm, key, and claims.

This is the primary function for generating JWT tokens. It creates a complete JWT token by encoding the claims as the payload, applying any SignOptions for standard claims, and signing the result with the specified algorithm and key.

**Parameters**:

  • alg: Cryptographic algorithm to use for signing (HS256, RS256, ES256, etc.)
  • key: Private key material appropriate for the algorithm
  • claims: Payload data to include in the token (any JSON-serializable type)
  • opts: Optional SignOption implementations for standard claims

**Payload Security**: The claims payload is NOT encrypted by default and is base64-encoded only. Do not include sensitive information unless using encryption functions (see SignEncrypted and GCM for encrypted payloads).

**Supported Claim Types**:

  • jwt.Claims struct for standard claims
  • map[string]any for flexible custom claims
  • Custom structs with JSON tags
  • jwt.Map type alias for convenience
  • Any type that marshals to valid JSON

**SignOption Processing**: When SignOptions are provided, they are processed to create standard claims which are then merged with the provided claims. Standard claims from options take precedence over conflicting claims.

**Return Value**: Returns the complete JWT token as a []byte slice in the standard format: header.payload.signature (base64url-encoded segments).

**Error Conditions**:

  • Invalid algorithm or key combination
  • Claims that cannot be marshaled to JSON
  • SignOption processing failures
  • Cryptographic signing failures

Example usage:

// Basic token with standard claims only
standardClaims := jwt.Claims{
    Subject: "user123",
    Issuer:  "myapp.com",
    Expiry:  time.Now().Add(time.Hour).Unix(),
}
token, err := jwt.Sign(jwt.HS256, secretKey, standardClaims)

// Custom claims with manual timing
now := time.Now()
customClaims := map[string]any{
    "iat":      now.Unix(),
    "exp":      now.Add(15 * time.Minute).Unix(),
    "user_id":  "12345",
    "role":     "admin",
    "permissions": []string{"read", "write"},
}
token, err := jwt.Sign(jwt.HS256, secretKey, customClaims)

// Custom claims with SignOptions for standard claims
userClaims := jwt.Map{
    "username": "john_doe",
    "role":     "user",
    "email":    "john@example.com",
}
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.MaxAge(15 * time.Minute),
    jwt.Audience{"api-service"},
    jwt.Claims{Issuer: "myapp.com"})

// Custom struct with embedded standard claims
type UserToken struct {
    Username string `json:"username"`
    Role     string `json:"role"`
    jwt.Claims
}
userToken := UserToken{
    Username: "alice",
    Role:     "admin",
    Claims:   jwt.Claims{Subject: "user456"},
}
token, err := jwt.Sign(jwt.RS256, rsaPrivateKey, userToken,
    jwt.MaxAge(30 * time.Minute))

See Verify for token validation and SignEncrypted for encrypted payloads.

func SignEncrypted added in v0.0.3

func SignEncrypted(alg Alg, key PrivateKey, encrypt InjectFunc, claims any, opts ...SignOption) ([]byte, error)

SignEncrypted creates and signs a JWT token with encrypted payload.

This function extends Sign by adding payload encryption capability. The claims are marshaled to JSON, then encrypted using the provided encrypt function, and finally signed. This provides confidentiality for sensitive payload data.

**Parameters**:

  • alg: Cryptographic algorithm to use for signing (HS256, RS256, ES256, etc.)
  • key: Private key material appropriate for the signing algorithm
  • encrypt: Function to encrypt the marshaled payload (see InjectFunc)
  • claims: Payload data to include in the token (any JSON-serializable type)
  • opts: Optional SignOption implementations for standard claims

**Encryption Process**:

  1. Claims are processed and merged with SignOptions
  2. Merged claims are marshaled to JSON
  3. JSON payload is encrypted using the encrypt function
  4. Encrypted payload is base64url-encoded and signed

**Security Benefits**:

  • Payload confidentiality (claims are encrypted)
  • Protection of sensitive information in tokens
  • Prevents payload inspection without decryption
  • Maintains JWT structure and signature verification

**InjectFunc Signature**: The encrypt function receives []byte (marshaled claims) and returns []byte (encrypted data) and error. It's called after JSON marshaling but before base64url encoding and signing.

**Decryption**: Tokens created with SignEncrypted must be verified using VerifyEncrypted with the corresponding decrypt function.

Example usage:

// Using AES-GCM encryption (see GCM function)
encryptKey := []byte("my-32-byte-encryption-key-here!")
encrypt, decrypt := jwt.GCM(encryptKey, nil)

// Create encrypted token
sensitiveData := map[string]any{
    "ssn":           "123-45-6789",
    "credit_card":   "4111-1111-1111-1111",
    "bank_account":  "9876543210",
    "user_id":       "12345",
}

token, err := jwt.SignEncrypted(jwt.HS256, signingKey, encrypt,
    sensitiveData, jwt.MaxAge(time.Hour))

// Custom encryption function
customEncrypt := func(payload []byte) ([]byte, error) {
    // Your custom encryption logic here
    return encryptedData, nil
}

token, err := jwt.SignEncrypted(jwt.RS256, rsaKey, customEncrypt,
    userClaims, jwt.Audience{"secure-api"})

**Important Notes**:

  • The encrypt function is called AFTER claims marshaling
  • Both signing and encryption keys should be securely managed
  • Use GCM function for authenticated encryption with AES-GCM
  • Consider key rotation policies for both signing and encryption keys

See GCM for ready-to-use authenticated encryption and VerifyEncrypted for decryption and verification of encrypted tokens.

func SignEncryptedWithHeader added in v0.0.14

func SignEncryptedWithHeader(alg Alg, key PrivateKey, encrypt InjectFunc, claims any, customHeader any, opts ...SignOption) ([]byte, error)

SignEncryptedWithHeader creates and signs a JWT token with encrypted payload and custom headers.

This function combines the functionality of SignEncrypted and SignWithHeader, providing both payload encryption and custom header support in a single operation. It's ideal for scenarios requiring both payload confidentiality and header metadata.

**Parameters**:

  • alg: Cryptographic algorithm to use for signing (HS256, RS256, ES256, etc.)
  • key: Private key material appropriate for the signing algorithm
  • encrypt: Function to encrypt the marshaled payload (see InjectFunc)
  • claims: Payload data to include in the token (any JSON-serializable type)
  • customHeader: Additional header fields to include (any JSON-serializable type)
  • opts: Optional SignOption implementations for standard claims

**Processing Order**:

  1. SignOptions are processed and merged with claims
  2. Claims are marshaled to JSON
  3. JSON payload is encrypted using the encrypt function
  4. Custom header fields are merged with standard JWT headers
  5. Token is assembled and signed with the specified algorithm

**Combined Benefits**:

  • Payload confidentiality through encryption
  • Header metadata for token identification and routing
  • Standard claims management through SignOptions
  • Complete JWT structure with signature verification

**Security Considerations**:

  • Headers remain unencrypted and visible to all token bearers
  • Payload is encrypted and requires decryption function to access
  • Both signing and encryption keys must be securely managed
  • Custom headers should not contain sensitive information

**Use Cases**:

  • Multi-tenant systems with encrypted user data and tenant identification
  • Key rotation systems with encrypted payloads and key identifiers
  • Content-type specific tokens with encrypted sensitive data
  • Audit systems requiring both data protection and metadata

Example usage:

// Multi-tenant encrypted token with key identification
encryptKey := []byte("tenant-specific-encryption-key!")
encrypt, decrypt := jwt.GCM(encryptKey, nil)

tenantHeader := map[string]any{
    "kid":       "tenant-123-key-v2",
    "tenant_id": "tenant-123",
    "env":       "production",
}

sensitiveData := map[string]any{
    "user_ssn":     "123-45-6789",
    "bank_account": "9876543210",
    "salary":       75000,
}

token, err := jwt.SignEncryptedWithHeader(jwt.RS256, rsaKey, encrypt,
    sensitiveData, tenantHeader, jwt.MaxAge(time.Hour))

// Key rotation with encrypted payload
rotationHeader := jwt.Map{
    "kid": "rotation-key-2023-q4",
    "ver": "2.0",
    "cty": "sensitive+json",
}

encryptedUserData := map[string]any{
    "personal_data": sensitiveUserInfo,
    "permissions":   userPermissions,
}

token, err := jwt.SignEncryptedWithHeader(jwt.ES256, ecdsaKey, encrypt,
    encryptedUserData, rotationHeader,
    jwt.Audience{"secure-api", "admin-panel"},
    jwt.Claims{Issuer: "auth-service"})

**Decryption and Verification**: Tokens created with this function must be verified using VerifyEncrypted with the corresponding decrypt function. Custom headers can be accessed from the returned VerifiedToken structure.

See GCM for authenticated encryption, VerifyEncrypted for token verification, and SignWithHeader for header-only customization without encryption.

func SignWithHeader added in v0.0.14

func SignWithHeader(alg Alg, key PrivateKey, claims any, customHeader any, opts ...SignOption) ([]byte, error)

SignWithHeader creates and signs a JWT token with custom header fields.

This function extends Sign by allowing custom fields to be added to the JWT header in addition to the standard "alg" and "typ" fields. Custom headers are useful for adding metadata or additional algorithm parameters.

**Parameters**:

  • alg: Cryptographic algorithm to use for signing (HS256, RS256, ES256, etc.)
  • key: Private key material appropriate for the signing algorithm
  • claims: Payload data to include in the token (any JSON-serializable type)
  • customHeader: Additional header fields to include (any JSON-serializable type)
  • opts: Optional SignOption implementations for standard claims

**Header Structure**: The JWT header will contain:

  • Standard fields: "alg" (algorithm), "typ" (always "JWT")
  • Custom fields: Any fields from the customHeader parameter
  • Conflict resolution: Custom fields override standard fields except "alg"

**Common Custom Header Use Cases**:

  • "kid" (Key ID): Identifies which key was used for signing
  • "x5t" (X.509 Thumbprint): Certificate thumbprint
  • "jku" (JWK Set URL): URL pointing to JWK Set
  • "cty" (Content Type): Content type of the secured payload
  • Custom application-specific metadata

**Security Considerations**:

  • Header fields are not encrypted and are visible to token bearers
  • Do not include sensitive information in custom headers
  • Validate custom header fields during token verification
  • Be cautious with fields that could affect security decisions

Example usage:

// Token with key identifier
customHeader := map[string]any{
    "kid": "key-2023-01",
    "x5t": "dGhpcyBpcyBhIFNIQTEgdGVzdA",
}

token, err := jwt.SignWithHeader(jwt.RS256, rsaKey, userClaims,
    customHeader, jwt.MaxAge(time.Hour))

// Header with JWK Set URL
headerWithJKU := jwt.Map{
    "kid": "rsa-key-1",
    "jku": "https://myapp.com/.well-known/jwks.json",
}

token, err := jwt.SignWithHeader(jwt.RS256, rsaKey, userClaims,
    headerWithJKU, jwt.Audience{"api-service"})

// Custom content type for non-JSON payload
headerWithCty := map[string]any{
    "cty": "example-content",
    "kid": "hmac-key-1",
}

token, err := jwt.SignWithHeader(jwt.HS256, hmacKey, customPayload,
    headerWithCty, jwt.MaxAge(30 * time.Minute))

// Struct-based custom header
type CustomHeader struct {
    KeyID     string `json:"kid"`
    Algorithm string `json:"alg"` // Will be overridden by actual algorithm
    Version   string `json:"ver"`
}

header := CustomHeader{
    KeyID:   "signing-key-v2",
    Version: "2.0",
}

token, err := jwt.SignWithHeader(jwt.ES256, ecdsaKey, userClaims,
    header, jwt.Claims{Issuer: "myapp.com"})

**Header Merging**: Custom header fields are merged with standard JWT header fields. The "alg" field is always set to the specified algorithm and cannot be overridden by custom headers.

func UnmarshalWithRequired

func UnmarshalWithRequired(payload []byte, dest any) error

UnmarshalWithRequired provides JSON unmarshaling with required field validation.

This function extends the standard unmarshaling behavior by validating that all fields marked with the "required" JSON tag are present and non-empty in the JWT payload. It's particularly useful for enforcing strict claim requirements in security-critical applications.

**Required Field Validation**:

  • Checks struct fields tagged with `json:"fieldname,required"`
  • Validates that required fields are present in the JSON payload
  • Ensures required fields have non-zero values (not nil, empty string, etc.)
  • Returns ErrMissingKey if any required field is missing or empty

**Usage Pattern**: Replace the global Unmarshal function to enable required field validation for all token verification operations in the application.

**Field Tag Format**: Use standard JSON tags with "required" option:

`json:"field_name,required"` - Field is required and must be present
`json:"field_name,omitempty,required"` - Cannot combine omitempty with required

**Validation Rules**:

  • String fields: Must not be empty ("")
  • Numeric fields: Must not be zero value (0, 0.0)
  • Boolean fields: Must be explicitly set (false is valid)
  • Slice/Map fields: Must not be nil or empty
  • Pointer fields: Must not be nil
  • Interface fields: Must not be nil

**Performance**: Adds reflection-based validation overhead after unmarshaling. Consider the performance impact for high-throughput applications.

Parameters:

  • payload: Raw JSON bytes from the JWT payload
  • dest: Pointer to destination struct with required field tags

Returns:

  • error: JSON unmarshaling errors or ErrMissingKey for missing required fields

Example usage:

// Define claims struct with required fields
type UserClaims struct {
    Username  string    `json:"username,required"`    // Must be present and non-empty
    UserID    int       `json:"user_id,required"`     // Must be present and non-zero
    Role      string    `json:"role,required"`        // Must be present and non-empty
    Email     string    `json:"email"`                // Optional field
    IssuedAt  time.Time `json:"iat,required"`         // Must be present
}

// Enable required field validation globally
jwt.Unmarshal = jwt.UnmarshalWithRequired

// Verify token - will fail if required fields are missing
var claims UserClaims
err := jwt.Verify(jwt.HS256, secretKey, token, &claims)
if err != nil {
    // Could be ErrMissingKey if required fields are missing
    log.Printf("Token verification failed: %v", err)
}

// Token with missing required field will fail:
// {"user_id": 123, "email": "user@example.com"} // Missing username and role

// Valid token:
// {"username": "john", "user_id": 123, "role": "admin", "iat": 1609459200}

Types

type AWSCognitoError added in v0.0.14

type AWSCognitoError struct {
	StatusCode int
	Message    string `json:"message"`
}

AWSCognitoError represents an error response from AWS Cognito. It implements the error interface.

func (AWSCognitoError) Error added in v0.0.14

func (e AWSCognitoError) Error() string

Error returns the error message.

type Alg

type Alg interface {
	// Name returns the algorithm identifier for the JWT "alg" header field.
	//
	// This value must match the standard algorithm names defined in RFC 7518
	// or be a custom identifier for non-standard algorithms. The name is
	// case-sensitive and used for algorithm selection during verification.
	//
	// **Standard Names**:
	//   - "HS256", "HS384", "HS512" for HMAC
	//   - "RS256", "RS384", "RS512" for RSA PKCS#1 v1.5
	//   - "PS256", "PS384", "PS512" for RSA-PSS
	//   - "ES256", "ES384", "ES512" for ECDSA
	//   - "EdDSA" for Ed25519
	//   - "none" for unsecured tokens
	//
	// Example usage:
	//
	//	alg := jwt.HS256
	//	fmt.Println(alg.Name()) // Output: "HS256"
	Name() string

	// Sign creates a cryptographic signature for the JWT.
	//
	// This method takes a private key and the concatenated base64url-encoded
	// header and payload (separated by a dot) and produces a signature.
	// The signature is returned as raw bytes (not base64url-encoded).
	//
	// **Parameters**:
	//   - key: Private key material (type depends on algorithm)
	//   - headerAndPayload: Base64url-encoded "header.payload" string
	//
	// **Key Types by Algorithm**:
	//   - HMAC: []byte (shared secret)
	//   - RSA: *rsa.PrivateKey
	//   - ECDSA: *ecdsa.PrivateKey
	//   - EdDSA: ed25519.PrivateKey
	//
	// **Security Requirements**:
	//   - Must validate key type and size
	//   - Should use cryptographically secure randomness
	//   - Must handle errors securely without information leakage
	//
	// **Error Conditions**:
	//   - ErrInvalidKey: Wrong key type for algorithm
	//   - Other errors: Cryptographic failures, insufficient entropy
	//
	// Example usage:
	//
	//	headerAndPayload := []byte("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0")
	//	signature, err := alg.Sign(secretKey, headerAndPayload)
	//	if err != nil {
	//	    log.Printf("Signing failed: %v", err)
	//	    return
	//	}
	Sign(key PrivateKey, headerAndPayload []byte) ([]byte, error)

	// Verify validates a JWT signature against the expected value.
	//
	// This method takes a public key, the original data that was signed,
	// and the signature to verify. It returns nil if verification succeeds
	// or an error if verification fails.
	//
	// **Parameters**:
	//   - key: Public key material (type depends on algorithm)
	//   - headerAndPayload: Base64url-encoded "header.payload" string (same as used for signing)
	//   - signature: Raw signature bytes (base64url-decoded from JWT)
	//
	// **Key Types by Algorithm**:
	//   - HMAC: []byte (same shared secret as signing)
	//   - RSA: *rsa.PublicKey
	//   - ECDSA: *ecdsa.PublicKey
	//   - EdDSA: ed25519.PublicKey
	//
	// **Security Requirements**:
	//   - Must use constant-time comparison to prevent timing attacks
	//   - Must validate key type and parameters
	//   - Should not leak information through error messages or timing
	//
	// **Error Conditions**:
	//   - ErrTokenSignature: Signature verification failed
	//   - ErrInvalidKey: Wrong key type for algorithm
	//   - Other errors: Malformed signature, cryptographic failures
	//
	// Example usage:
	//
	//	err := alg.Verify(publicKey, headerAndPayload, signature)
	//	if err == jwt.ErrTokenSignature {
	//	    log.Printf("Invalid signature detected")
	//	    return errors.New("authentication failed")
	//	}
	//	if err != nil {
	//	    log.Printf("Verification error: %v", err)
	//	    return err
	//	}
	//	// Token is valid
	Verify(key PublicKey, headerAndPayload []byte, signature []byte) error
}

Alg represents a cryptographic algorithm for JWT signing and verification.

This interface defines the contract that all JWT algorithms must implement. It provides a unified API for different cryptographic approaches (symmetric and asymmetric) while abstracting the underlying implementation details.

**Algorithm Categories**:

  • Symmetric: HMAC algorithms (HS256, HS384, HS512) using shared secrets
  • Asymmetric: RSA, ECDSA, EdDSA using public/private key pairs
  • Unsecured: None algorithm for testing and specific use cases

**Implementation Requirements**:

  • Thread-safe operations for concurrent use
  • Constant-time signature verification to prevent timing attacks
  • Proper error handling for invalid keys and malformed data
  • RFC 7518 compliance for standard algorithms

**Security Considerations**:

  • Implementations must validate key types and sizes
  • Signature operations should use cryptographically secure randomness
  • Timing-sensitive operations should be constant-time
  • Error messages should not leak cryptographic information

Example custom algorithm implementation:

type CustomAlg struct {
    name string
}

func (a *CustomAlg) Name() string {
    return a.name
}

func (a *CustomAlg) Sign(key PrivateKey, data []byte) ([]byte, error) {
    // Custom signing logic
    return signature, nil
}

func (a *CustomAlg) Verify(key PublicKey, data, sig []byte) error {
    // Custom verification logic
    return nil
}

**Built-in Algorithms**: The library provides implementations for all standard JWT algorithms. Custom algorithms can be created by implementing this interface.

var (
	// NONE represents the "none" algorithm for unsecured JWTs.
	//
	// **SECURITY WARNING**: This algorithm provides NO cryptographic security.
	// Tokens signed with NONE can be modified by anyone without detection.
	// Use ONLY in specific scenarios where security is handled by other means.
	//
	// **Valid Use Cases**:
	//   - Client-side data storage where tampering doesn't matter
	//   - Development and testing environments
	//   - Public information distribution
	//   - Session data that's validated by other mechanisms
	//
	// **Invalid Use Cases**:
	//   - Authentication tokens
	//   - Authorization decisions
	//   - Any security-sensitive data
	//   - Production environments (generally)
	//
	// **Example Scenario**: Single-page application storing user preferences
	// and navigation state. Even if modified, no security impact occurs since
	// all security decisions are made server-side with separate authentication.
	//
	// Example payload (safe for NONE algorithm):
	//
	//	{
	//	  "sub": "user123",
	//	  "session": "ch72gsb320000udocl363eofy",
	//	  "displayName": "John Doe",
	//	  "lastPage": "/dashboard",
	//	  "theme": "dark",
	//	  "language": "en"
	//	}
	//
	// **Implementation**: Always returns empty signature and accepts any signature
	// as valid. The verification process succeeds for any input.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.6 as the "none" algorithm.
	NONE Alg = &algNONE{}

	// HS256 uses HMAC with SHA-256 hash function.
	//
	// **Security Level**: 128-bit security
	// **Key Requirement**: Minimum 32 bytes (256 bits)
	// **Hash Output**: 32 bytes
	// **Performance**: Fastest JWT algorithm
	//
	// **Most Common Choice**: HS256 is the most widely used JWT algorithm
	// due to its excellent balance of security and performance.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.2
	HS256 Alg = &algHMAC{"HS256", crypto.SHA256}

	// HS384 uses HMAC with SHA-384 hash function.
	//
	// **Security Level**: 192-bit security
	// **Key Requirement**: Minimum 48 bytes (384 bits)
	// **Hash Output**: 48 bytes
	// **Performance**: Slightly slower than HS256, faster than HS512
	//
	// **Use Case**: Higher security requirements than HS256 while
	// maintaining good performance characteristics.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.2
	HS384 Alg = &algHMAC{"HS384", crypto.SHA384}

	// HS512 uses HMAC with SHA-512 hash function.
	//
	// **Security Level**: 256-bit security
	// **Key Requirement**: Minimum 64 bytes (512 bits)
	// **Hash Output**: 64 bytes
	// **Performance**: Slowest HMAC variant, still faster than asymmetric algorithms
	//
	// **Use Case**: Maximum security in symmetric algorithm family.
	// Larger signatures may impact network performance.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.2
	HS512 Alg = &algHMAC{"HS512", crypto.SHA512}

	// RS256 uses RSA with SHA-256 hash and PKCS#1 v1.5 padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-256
	// **Padding**: PKCS#1 v1.5
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Most Popular Asymmetric Algorithm**: RS256 is the most widely
	// used asymmetric JWT algorithm due to broad support and compatibility.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	RS256 Alg = &algRSA{"RS256", crypto.SHA256}

	// RS384 uses RSA with SHA-384 hash and PKCS#1 v1.5 padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-384
	// **Padding**: PKCS#1 v1.5
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Use Case**: Higher hash security than RS256 while maintaining
	// RSA algorithm compatibility.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	RS384 Alg = &algRSA{"RS384", crypto.SHA384}

	// RS512 uses RSA with SHA-512 hash and PKCS#1 v1.5 padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-512
	// **Padding**: PKCS#1 v1.5
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Use Case**: Maximum hash security in RSA PKCS#1 v1.5 family.
	// Larger signature size may impact performance.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	RS512 Alg = &algRSA{"RS512", crypto.SHA512}

	// PS256 uses RSA with SHA-256 hash and PSS padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-256
	// **Padding**: PSS with automatic salt length
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Recommended Choice**: PS256 provides the best balance of security,
	// performance, and compatibility for new RSA-based JWT implementations.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	PS256 Alg = &algRSAPSS{"PS256", &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA256}}

	// PS384 uses RSA with SHA-384 hash and PSS padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-384
	// **Padding**: PSS with automatic salt length
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Use Case**: Higher hash security than PS256 with enhanced
	// PSS padding security properties.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	PS384 Alg = &algRSAPSS{"PS384", &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA384}}

	// PS512 uses RSA with SHA-512 hash and PSS padding.
	//
	// **Security Level**: 112-bit security (2048-bit keys)
	// **Hash Function**: SHA-512
	// **Padding**: PSS with automatic salt length
	// **Key Size**: Minimum 2048 bits recommended
	//
	// **Use Case**: Maximum hash and padding security in the RSA family.
	// Provides the highest security level for RSA-based JWT algorithms.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.3
	PS512 Alg = &algRSAPSS{"PS512", &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA512}}

	// ES256 uses ECDSA with P-256 curve and SHA-256 hash.
	//
	// **Security Level**: 128-bit security
	// **Curve**: P-256 (secp256r1/prime256v1)
	// **Hash Function**: SHA-256
	// **Key Size**: 256-bit curve (32-byte coordinates)
	//
	// **Most Popular ECDSA Algorithm**: ES256 provides excellent balance
	// of security, performance, and compatibility. Widely supported and
	// recommended for most ECDSA use cases.
	//
	// **Token Size**: Approximately 3 times smaller than equivalent RSA tokens.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.4
	ES256 Alg = &algECDSA{"ES256", crypto.SHA256, 32, 256}

	// ES384 uses ECDSA with P-384 curve and SHA-384 hash.
	//
	// **Security Level**: 192-bit security
	// **Curve**: P-384 (secp384r1)
	// **Hash Function**: SHA-384
	// **Key Size**: 384-bit curve (48-byte coordinates)
	//
	// **Use Case**: Higher security than ES256 while maintaining ECDSA
	// performance advantages. Good choice for high-security applications.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.4
	ES384 Alg = &algECDSA{"ES384", crypto.SHA384, 48, 384}

	// ES512 uses ECDSA with P-521 curve and SHA-512 hash.
	//
	// **Security Level**: 256-bit security
	// **Curve**: P-521 (secp521r1)
	// **Hash Function**: SHA-512
	// **Key Size**: 521-bit curve (66-byte coordinates)
	//
	// **Use Case**: Maximum security in ECDSA family. Provides the highest
	// security level available in standard ECDSA algorithms.
	//
	// **Note**: Despite the name "ES512", this uses the P-521 curve (521 bits),
	// not a 512-bit curve. The naming follows the hash function.
	//
	// **Compliance**: Defined in RFC 7518 Section 3.4
	ES512 Alg = &algECDSA{"ES512", crypto.SHA512, 66, 521}
	// EdDSA represents the Edwards-curve Digital Signature Algorithm using Ed25519.
	//
	// **Algorithm Family**: Edwards-curve Digital Signature Algorithm
	// **Sign Key**: ed25519.PrivateKey (64 bytes)
	// **Verify Key**: ed25519.PublicKey (32 bytes)
	// **Security Model**: Asymmetric - different keys for signing and verification
	// **Algorithm Name**: "EdDSA" (in JWT header)
	//
	// **Modern Cryptographic Algorithm**: EdDSA represents the latest generation
	// of elliptic curve cryptography, offering significant advantages over both
	// traditional ECDSA and RSA algorithms.
	//
	// **Key Advantages**:
	//   - Exceptional performance (comparable to or better than ECDSA)
	//   - Strong security guarantees and resistance to side-channel attacks
	//   - Deterministic signatures (same message always produces same signature)
	//   - Simple implementation with fewer opportunities for errors
	//   - No need for secure random number generation during signing
	//   - Immunity to certain classes of implementation vulnerabilities
	//   - Fast verification suitable for high-throughput scenarios
	//
	// **Security Properties**:
	//   - 128-bit security level (equivalent to RSA-3072 or ECDSA P-256)
	//   - Resistant to timing attacks by design
	//   - No malleable signatures
	//   - Strong unforgeability guarantees
	//   - Collision-resistant and second-preimage resistant
	//
	// **Key and Signature Sizes**:
	//   - Private Key: 64 bytes (includes 32-byte seed + 32-byte public key)
	//   - Public Key: 32 bytes (very compact)
	//   - Signature: 64 bytes (smaller than equivalent ECDSA signatures)
	//   - Total overhead significantly smaller than RSA
	//
	// **Performance Characteristics**:
	//   - Signing: Very fast, deterministic (no random number generation)
	//   - Verification: Extremely fast, often faster than ECDSA
	//   - Key generation: Fast and simple
	//   - Batch verification: Excellent performance for multiple signatures
	//
	// **Ed25519 Curve Properties**:
	//   - Uses the Edwards25519 elliptic curve
	//   - Designed specifically for high performance and security
	//   - Avoids many pitfalls of other elliptic curves
	//   - No known cryptographic weaknesses
	//
	// **Key Generation in Go**:
	//
	//	// Generate Ed25519 key pair
	//	publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
	//	if err != nil {
	//	    log.Fatal("Failed to generate Ed25519 key:", err)
	//	}
	//
	//	// Sign token
	//	token, err := jwt.Sign(jwt.EdDSA, privateKey, claims)
	//
	//	// Verify token
	//	verifiedToken, err := jwt.Verify(jwt.EdDSA, publicKey, token)
	//
	// **OpenSSL Support**: Ed25519 support was added in OpenSSL 1.1.1:
	//
	//	# Generate Ed25519 private key
	//	$ openssl genpkey -algorithm ed25519 -out ed25519_private_key.pem
	//
	//	# Extract public key
	//	$ openssl pkey -in ed25519_private_key.pem -pubout -out ed25519_public_key.pem
	//
	// **When to Use EdDSA**:
	//   - New systems with no legacy constraints
	//   - High-performance requirements
	//   - Security-critical applications
	//   - Mobile and IoT applications (small keys/signatures)
	//   - Systems requiring deterministic signatures
	//   - Applications needing resistance to side-channel attacks
	//
	// **Considerations**:
	//   - Newer algorithm with less ecosystem support than RSA/ECDSA
	//   - Requires Go 1.13+ for standard library support
	//   - May not be supported in older JWT libraries or validators
	//   - Limited HSM support compared to RSA/ECDSA
	//
	// **Standards Compliance**:
	//   - RFC 8037: CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JOSE
	//   - RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)
	//   - Widely adopted in modern cryptographic protocols
	//
	// **Recommendation**: EdDSA is the recommended choice for new applications
	// that can accommodate its requirements. It provides the best combination of
	// security, performance, and simplicity among asymmetric algorithms.
	EdDSA Alg = &algEdDSA{"EdDSA"}
)

Algorithm Selection Guide

**Quick Selection Guide**:

  • High Performance + Shared Secret: Use HMAC (HS256/HS384/HS512)
  • Public Key Infrastructure: Use RSA (RS256) or ECDSA (ES256)
  • Modern High Security: Use EdDSA (Ed25519)
  • Legacy RSA Systems: Use RSA-PSS (PS256) for enhanced security
  • Testing/Development Only: Use NONE (never in production)

**Algorithm Categories**:

1. **Symmetric Algorithms**: Single shared secret for signing and verification

  • Pros: Fast, simple key management, well-tested
  • Cons: Key distribution challenges, single point of failure
  • Use when: Both parties can securely share a secret

2. **Asymmetric Algorithms**: Separate keys for signing and verification

  • Pros: Better key distribution, non-repudiation, scalable
  • Cons: Slower performance, larger tokens, more complex key management
  • Use when: Need to distribute verification capability widely

**Security vs Performance Trade-offs**:

  • HMAC: Fastest, requires secure key sharing
  • ECDSA/EdDSA: Good performance, smaller keys/signatures than RSA
  • RSA: Widely supported, larger keys/signatures, moderate performance
  • RSA-PSS: Enhanced RSA security, similar performance to RSA

**Token Size Comparison** (approximate):

  • HMAC: ~200-300 bytes
  • ECDSA: ~300-400 bytes
  • EdDSA: ~250-350 bytes
  • RSA: ~500-800 bytes

type AlgParser added in v0.0.14

type AlgParser interface {
	// Parse converts raw key data into cryptographic key objects.
	//
	// This method attempts to parse private and/or public key data from
	// byte arrays into the appropriate Go cryptographic types for the algorithm.
	// At least one of the parameters should be non-empty.
	//
	// **Parameters**:
	//   - private: Raw private key data (PEM, DER, or other format)
	//   - public: Raw public key data (PEM, DER, or other format)
	//
	// **Return Values**:
	//   - PrivateKey: Parsed private key (nil if not provided or not needed)
	//   - PublicKey: Parsed public key (derived from private key if available)
	//   - error: Parsing error or nil on success
	//
	// **Supported Formats**: Implementations should support standard formats:
	//   - PEM encoding with appropriate headers
	//   - DER binary encoding
	//   - PKCS#1, PKCS#8, or SEC1 formats as appropriate
	//   - X.509 format for public keys
	//
	// **Key Derivation**: If a private key is provided, the public key
	// should be derived from it automatically. If only public key data
	// is provided, the private key should be nil.
	//
	// Example usage:
	//
	//	// Parse RSA key pair
	//	privKey, pubKey, err := rsaAlg.Parse(privateKeyPEM, nil)
	//	if err != nil {
	//	    log.Printf("Failed to parse RSA keys: %v", err)
	//	    return
	//	}
	//
	//	// Parse only public key
	//	_, pubKey, err = rsaAlg.Parse(nil, publicKeyPEM)
	//	if err != nil {
	//	    log.Printf("Failed to parse RSA public key: %v", err)
	//	    return
	//	}
	Parse(private, public []byte) (PrivateKey, PublicKey, error)
}

AlgParser is an optional interface for algorithms that support key parsing.

Algorithms can implement this interface to provide automatic key parsing from raw byte data. This is particularly useful for multi-key scenarios where keys are loaded from files, databases, or remote sources.

**Use Cases**:

  • Loading keys from PEM files
  • Parsing keys from configuration data
  • Automatic key format detection
  • Multi-key management systems
  • Key rotation and update scenarios

**Implementation**: Algorithms that support key parsing should implement this interface to provide seamless integration with key management systems. The parsing should handle common key formats and provide clear error messages for unsupported formats.

**Integration**: This interface is used by the kid_keys.go functionality to automatically parse and manage multiple keys with different algorithms.

Example implementation:

func (a *rsaAlg) Parse(private, public []byte) (PrivateKey, PublicKey, error) {
    var privKey *rsa.PrivateKey
    var pubKey *rsa.PublicKey
    var err error

    if len(private) > 0 {
        privKey, err = parseRSAPrivateKey(private)
        if err != nil {
            return nil, nil, err
        }
        pubKey = &privKey.PublicKey
    } else if len(public) > 0 {
        pubKey, err = parseRSAPublicKey(public)
        if err != nil {
            return nil, nil, err
        }
    }

    return privKey, pubKey, nil
}

**Error Handling**: Implementations should return descriptive errors that help identify the specific parsing failure (format, encoding, etc.).

type Audience added in v0.0.8

type Audience []string

Audience represents the "aud" (audience) claim for JWT tokens.

The audience claim identifies the intended recipients of the JWT token. Recipients should verify that they are included in the audience before accepting and processing the token. This provides an additional security layer by ensuring tokens are only used by their intended consumers.

**JWT Specification**: The "aud" claim can be either:

  • A single string value (single recipient)
  • An array of strings (multiple recipients)

This type handles both formats transparently during JSON marshaling/unmarshaling.

**Security Considerations**:

  • Always validate that your application/service is in the audience
  • Reject tokens where your identifier is not present in the audience
  • Use specific, non-ambiguous audience identifiers
  • Consider using URLs or URIs for globally unique audience values

**Common Usage Patterns**:

  • API service names: []string{"api-service", "user-service"}
  • Application domains: []string{"app.example.com", "admin.example.com"}
  • Service endpoints: []string{"https://api.example.com/v1"}
  • Role-based audiences: []string{"admin-users", "premium-subscribers"}

Example usage:

// Single audience
aud := jwt.Audience{"api-service"}

// Multiple audiences
aud := jwt.Audience{"api-service", "user-service", "admin-panel"}

// Use in claims
claims := jwt.Claims{
    Subject:  "user123",
    Audience: jwt.Audience{"api-service", "web-app"},
}

// Or as SignOption
token, err := jwt.Sign(jwt.HS256, key, userClaims, jwt.Audience{"api"})

// Validation
if !slices.Contains(claims.Audience, "my-service") {
    return errors.New("token not intended for this service")
}

func (Audience) ApplyClaims added in v0.0.14

func (aud Audience) ApplyClaims(dest *Claims)

ApplyClaims implements the SignOption interface to set audience claims during token signing.

This method allows Audience to be used as a SignOption parameter in Sign functions, providing a convenient way to specify intended token recipients during token creation. The audience will be automatically included in the token's standard claims.

**SignOption Interface**: This implementation enables Audience to be passed directly to signing functions alongside other options like MaxAge, custom claims, and other SignOption implementations.

**Usage Patterns**:

  • Single audience specification for dedicated services
  • Multiple audiences for tokens shared across services
  • Dynamic audience assignment based on user context
  • Integration with role-based access patterns

**Security Benefits**:

  • Ensures tokens are properly scoped to intended recipients
  • Enables fine-grained access control
  • Facilitates service-to-service authentication validation
  • Supports multi-tenant architectures

Example usage:

// Single audience for API access
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims, jwt.Audience{"api-service"})

// Multiple audiences for cross-service access
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.Audience{"api-service", "user-service", "admin-panel"},
    jwt.MaxAge(15 * time.Minute))

// Role-based audience assignment
var audiences jwt.Audience
if user.IsAdmin {
    audiences = jwt.Audience{"admin-api", "user-api"}
} else {
    audiences = jwt.Audience{"user-api"}
}
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims, audiences)

func (Audience) String added in v0.0.14

func (auth Audience) String() string

String returns a space-separated string representation of the audience.

This method provides a human-readable format for the audience claim, which is useful for logging, debugging, and display purposes. Multiple audience values are joined with space separators.

**Usage Scenarios**:

  • Logging audience information for debugging
  • Displaying token recipients in admin interfaces
  • Creating readable audit trails
  • Generating user-friendly error messages

**Output Format**: Space-separated string of all audience values

Example:

aud := jwt.Audience{"api-service", "web-app", "mobile-app"}
fmt.Println(aud.String()) // Output: "api-service web-app mobile-app"

// Single audience
aud2 := jwt.Audience{"api-service"}
fmt.Println(aud2.String()) // Output: "api-service"

// Empty audience
aud3 := jwt.Audience{}
fmt.Println(aud3.String()) // Output: ""

// Use in logging
log.Printf("Token intended for: %s", claims.Audience.String())

func (*Audience) UnmarshalJSON added in v0.0.8

func (aud *Audience) UnmarshalJSON(data []byte) (err error)

UnmarshalJSON implements the json.Unmarshaler interface for flexible audience parsing.

The JWT specification allows the "aud" claim to be either a single string or an array of strings. This method handles both formats transparently, normalizing them into a consistent slice representation.

**Supported Input Formats**:

  • Single string: "api-service" becomes ["api-service"]
  • Array of strings: ["api", "web"] remains ["api", "web"]
  • Empty/null values are handled gracefully

**Implementation Details**:

  • Detects format by examining the first byte of JSON data
  • Uses standard json.Unmarshal for actual parsing
  • Maintains compatibility with various JWT implementations
  • Handles edge cases like empty arrays and null values

This ensures compatibility with JWT tokens from different sources that may use either format for the audience claim, providing a consistent interface for audience validation regardless of the original format.

Example JSON inputs:

  • "aud": "single-service" -> Audience{"single-service"}
  • "aud": ["service1", "service2"] -> Audience{"service1", "service2"}
  • "aud": [] -> Audience{} (empty)
  • "aud": null -> Audience{} (empty)

type Blocklist

type Blocklist struct {
	Clock func() time.Time
	// GetKey is a function which can be used how to extract
	// the unique identifier for a token, by default
	// it checks if the "jti" is not empty, if it's then the key is the token itself.
	GetKey func(token []byte, claims Claims) string
	// contains filtered or unexported fields
}

Blocklist is an in-memory storage system for invalidated JWT tokens. It provides server-side token revocation capabilities, which is essential for scenarios like user logout, account suspension, or security breaches.

The Blocklist maintains a thread-safe map of token identifiers to expiration times, automatically cleaning up expired entries to prevent memory leaks.

While client-side token removal is the most common invalidation method, server-side blocklisting provides an additional security layer for cases where:

  • Users cannot be trusted to remove tokens
  • Tokens may have been compromised
  • Immediate revocation is required

Custom storage backends (Redis, database, etc.) can be implemented by satisfying the TokenValidator interface for distributed applications.

Example:

// Create a blocklist with hourly cleanup
blocklist := jwt.NewBlocklist(1 * time.Hour)

// Use in token verification
verifiedToken, err := jwt.Verify(alg, key, token, blocklist)

// Invalidate a token (e.g., on logout)
err = blocklist.InvalidateToken(token, verifiedToken.StandardClaims)

func NewBlocklist

func NewBlocklist(gcEvery time.Duration) *Blocklist

NewBlocklist creates a new in-memory token blocklist with automatic garbage collection.

The gcEvery parameter controls how frequently expired tokens are removed from memory. A good value is typically the same as your token expiration time (e.g., 1 hour). Pass 0 to disable automatic garbage collection.

The returned Blocklist implements the TokenValidator interface and can be passed directly to Verify functions.

Example:

// Cleanup every hour
blocklist := jwt.NewBlocklist(1 * time.Hour)

// No automatic cleanup (manual GC required)
blocklist := jwt.NewBlocklist(0)

func NewBlocklistContext

func NewBlocklistContext(ctx context.Context, gcEvery time.Duration) *Blocklist

NewBlocklistContext creates a new in-memory token blocklist with context-aware garbage collection.

This function is identical to NewBlocklist but accepts a context for controlling the garbage collection goroutine lifecycle. When the context is canceled, the GC goroutine will stop gracefully.

This is useful in applications where you need to coordinate shutdown or want to control the blocklist lifecycle explicitly.

Example:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blocklist := jwt.NewBlocklistContext(ctx, 1*time.Hour)
// GC will stop when cancel() is called

func (*Blocklist) Count

func (b *Blocklist) Count() (int64, error)

Count returns the total number of currently blocked tokens in memory. This can be useful for monitoring and debugging purposes.

func (*Blocklist) Del

func (b *Blocklist) Del(key string) error

Del removes a token from the blocklist by its key. This method can be used to manually unblock a token or for cleanup operations.

The key should be the same identifier used by the GetKey function (typically the "jti" claim or the full token).

func (*Blocklist) GC

func (b *Blocklist) GC() int

GC performs garbage collection by removing expired tokens from the blocklist.

This method compares each token's expiration time against the current time and removes entries that have naturally expired. This prevents memory leaks in long-running applications.

Returns the number of tokens that were removed.

While automatic GC is typically enabled via NewBlocklist, this method can be called manually for:

  • Applications with custom GC scheduling requirements
  • Memory pressure situations requiring immediate cleanup
  • Testing and debugging scenarios

Example:

// Manual cleanup
removed := blocklist.GC()
log.Printf("Cleaned up %d expired tokens", removed)

func (*Blocklist) Has

func (b *Blocklist) Has(key string) (bool, error)

Has checks whether a token key is currently blocked.

This method performs a read-only check without modifying the blocklist. It's primarily used internally by ValidateToken, but can also be used for external checks or debugging.

Returns false if the key is empty, true if the key is found in the blocklist.

func (*Blocklist) InvalidateToken

func (b *Blocklist) InvalidateToken(token []byte, c Claims) error

InvalidateToken adds a JWT token to the blocklist, preventing its future use.

This method extracts the token's unique identifier using the configured GetKey function and stores it with the token's expiration time for automatic cleanup.

Common use cases:

  • User logout when client-side token removal cannot be guaranteed
  • Immediate token revocation due to security concerns
  • Account suspension or privilege changes
  • Compromised token scenarios

The token will be blocked until its natural expiration time, after which it will be automatically removed during garbage collection.

Example:

// After successful logout
err := blocklist.InvalidateToken(token, verifiedToken.StandardClaims)
if err != nil {
    log.Printf("Failed to blocklist token: %v", err)
}

func (*Blocklist) ValidateToken

func (b *Blocklist) ValidateToken(token []byte, c Claims, err error) error

ValidateToken implements the TokenValidator interface. It checks if the token is present in the blocklist and returns ErrBlocked if found.

This method also performs automatic cleanup by removing expired blocked tokens when they encounter an ErrExpired error during normal validation.

The validation flow:

  1. If there's a previous validation error (like expiration), handle cleanup
  2. Check if the token key exists in the blocklist
  3. Return ErrBlocked if found, otherwise allow the token

type Claims

type Claims struct {
	// NotBefore represents the "nbf" (not before) claim as Unix timestamp.
	// Defines the earliest time the token is considered valid. Tokens cannot
	// be accepted before this time to prevent premature usage.
	NotBefore int64 `json:"nbf,omitempty"`

	// IssuedAt represents the "iat" (issued at) claim as Unix timestamp.
	// Records when the token was created. Used for age validation and
	// detecting tokens issued in the future.
	IssuedAt int64 `json:"iat,omitempty"`

	// Expiry represents the "exp" (expiration) claim as Unix timestamp.
	// Defines when the token becomes invalid. Critical for security
	// as it limits token lifetime and prevents indefinite usage.
	Expiry int64 `json:"exp,omitempty"`

	// ID represents the "jti" (JWT ID) claim.
	// Unique identifier for this token, useful for tracking, blacklisting,
	// and preventing replay attacks. Must be unique per issuer.
	ID string `json:"jti,omitempty"`

	// OriginID represents a custom "origin_jti" claim (non-standard).
	// May reference a parent token's ID for tracking token hierarchies,
	// refresh chains, or related token invalidation scenarios.
	OriginID string `json:"origin_jti,omitempty"`

	// Issuer represents the "iss" (issuer) claim.
	// Identifies the principal that issued the token. Can be a URL,
	// domain name, or other unique identifier for the token authority.
	Issuer string `json:"iss,omitempty"`

	// Subject represents the "sub" (subject) claim.
	// Identifies the principal that is the subject of the token
	// (typically the user). Must be unique within the issuer's context.
	Subject string `json:"sub,omitempty"`

	// Audience represents the "aud" (audience) claim.
	// Identifies the intended recipients of the token. Recipients must
	// validate they are included in the audience before accepting the token.
	Audience Audience `json:"aud,omitempty"`
}

Claims represents the standard JWT claims (registered claims) as defined by RFC 7519.

This structure contains the standardized fields that provide common token metadata and timing controls. It implements the SignOption interface, allowing it to be passed directly to Sign functions to set standard claims automatically.

**Standard Claims Included**:

  • Timing claims: nbf (not before), iat (issued at), exp (expiry)
  • Identity claims: iss (issuer), sub (subject), aud (audience)
  • Tracking claims: jti (JWT ID)
  • Extension: origin_jti (non-standard origin tracking)

**Usage Patterns**:

  • Embed in custom claim structures for type safety
  • Use directly for simple tokens with only standard claims
  • Pass as SignOption to automatically apply standard claims
  • Validate using built-in time-based validation methods

Example usage:

// Direct usage
claims := jwt.Claims{
    Subject:  "user123",
    Issuer:   "myapp.com",
    Audience: jwt.Audience{"api", "web"},
    Expiry:   time.Now().Add(time.Hour).Unix(),
    IssuedAt: time.Now().Unix(),
}

// As SignOption
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims, claims)

// Embedded in custom struct
type UserClaims struct {
    Username string `json:"username"`
    Role     string `json:"role"`
    jwt.Claims
}

func (Claims) Age added in v0.0.4

func (c Claims) Age() time.Duration

Age returns the total lifetime duration of the token based on its claims.

This method calculates the intended lifespan of the token by computing the difference between the expiration time ("exp") and issued time ("iat"). This represents the maximum duration the token was designed to be valid.

**Calculation**: expiry_time - issued_time = token_lifetime

**Use Cases**:

  • Token lifetime analysis for security auditing
  • Monitoring token usage patterns and lifespans
  • Validating token configuration policies
  • Debugging token expiration issues
  • Generating metrics for token management

**Return Value**:

  • Positive duration: Normal token with valid lifetime
  • Zero duration: Token with missing or invalid timing claims
  • Negative duration: Invalid token with expiry before issue time

**Important Notes**:

  • This returns the designed lifetime, not remaining time (use Timeleft for that)
  • Zero values in timing claims will result in zero or incorrect duration
  • Does not account for clock skew between issuer and current system

Example usage:

claims := jwt.Claims{
    IssuedAt: time.Now().Unix(),
    Expiry:   time.Now().Add(time.Hour).Unix(),
}

lifetime := claims.Age()
fmt.Printf("Token designed for: %v", lifetime) // Output: 1h0m0s

// Use for monitoring
if claims.Age() > 24*time.Hour {
    log.Printf("Long-lived token detected: %v", claims.Age())
}

func (Claims) ApplyClaims

func (c Claims) ApplyClaims(dest *Claims)

ApplyClaims implements the SignOption interface to merge standard claims during token signing.

This method allows Claims to be used as a SignOption parameter in Sign functions, enabling automatic application of standard JWT claims during token creation. Only non-zero and non-empty values are applied, allowing selective claim setting.

**SignOption Interface**: This implementation enables Claims structs to be passed directly to signing functions alongside other options like MaxAge, Audience, and custom claim structures.

**Selective Application**: The method only applies claims that have meaningful values:

  • Timing claims (NotBefore, IssuedAt, Expiry): Applied only if > 0
  • String claims (ID, OriginID, Issuer, Subject): Applied only if not empty
  • Audience claim: Applied only if slice has length > 0

**Non-Destructive Merging**: This method merges claims into the destination without overwriting existing values unnecessarily. Zero values are considered "unset" and are skipped during the merge process.

**Usage Patterns**:

  • Template claims for consistent token issuance
  • Default claim values for all tokens from an issuer
  • Partial claim updates during token renewal
  • Combining multiple claim sources

Example usage:

// Define standard claims for your application
standardClaims := jwt.Claims{
    Issuer:  "myapp.com",
    Subject: "user123",
    Expiry:  time.Now().Add(time.Hour).Unix(),
}

// Use as SignOption with custom claims
customClaims := map[string]any{
    "role":     "admin",
    "username": "john_doe",
}

token, err := jwt.Sign(jwt.HS256, secretKey, customClaims, standardClaims)

// Or combine with other SignOptions
token, err := jwt.Sign(jwt.HS256, secretKey, customClaims,
    standardClaims,
    jwt.Audience{"api-service"},
    jwt.MaxAge(30 * time.Minute))

func (Claims) ExpiresAt added in v0.0.4

func (c Claims) ExpiresAt() time.Time

ExpiresAt returns the time when this token will expire.

This method converts the Unix timestamp stored in the Expiry field to a time.Time value, providing a convenient way to work with expiration times in Go's time package format. The returned time is rounded to the nearest second.

**Conversion Details**:

  • Uses time.Unix() to convert the Unix timestamp to time.Time
  • Nanosecond component is set to 0 (second precision)
  • Handles zero values gracefully (returns Unix epoch if Expiry is 0)

**Use Cases**:

  • Comparing expiration time with current time
  • Calculating time remaining until expiration
  • Formatting expiration time for display
  • Time-based conditional logic
  • Integration with time-based APIs

**Zero Value Behavior**:

  • If Expiry is 0, returns time.Unix(0, 0) (Unix epoch: 1970-01-01 00:00:00 UTC)
  • This typically indicates a token without an expiration time

Example usage:

claims := jwt.Claims{
    Expiry: time.Now().Add(time.Hour).Unix(),
}

expireTime := claims.ExpiresAt()
fmt.Printf("Token expires: %v", expireTime.Format(time.RFC3339))

// Check if token is expired
if time.Now().After(claims.ExpiresAt()) {
    fmt.Println("Token has expired")
}

// Calculate time until expiration
timeLeft := claims.ExpiresAt().Sub(time.Now())
fmt.Printf("Time remaining: %v", timeLeft)

func (Claims) Timeleft added in v0.0.4

func (c Claims) Timeleft() time.Duration

Timeleft returns the remaining time until the token expires.

This method calculates how much time is left before the token becomes invalid by computing the difference between the expiration time and the current time. The calculation uses the Clock() function to get the current time, allowing for consistent time handling across the JWT library.

**Calculation**: expiry_time - current_time = remaining_time

**Return Values**:

  • Positive duration: Token is still valid, shows time remaining
  • Zero duration: Token has just expired or has no expiry set
  • Negative duration: Token has already expired

**Use Cases**:

  • Pre-expiration warnings and refresh logic
  • Token lifetime monitoring and metrics
  • Conditional token renewal decisions
  • User interface countdown displays
  • Proactive token management in applications

**Important Notes**:

  • Uses the configurable Clock() function for current time
  • Returns duration rounded to the nearest second
  • Zero Expiry field results in zero duration (no expiration)
  • Negative values indicate already expired tokens

**Clock Function**: The calculation uses the package-level Clock variable, which can be customized for testing or specific time zone requirements.

Example usage:

claims := jwt.Claims{
    Expiry: time.Now().Add(15 * time.Minute).Unix(),
}

remaining := claims.Timeleft()
if remaining > 5*time.Minute {
    fmt.Println("Token has plenty of time left")
} else if remaining > 0 {
    fmt.Printf("Token expires soon: %v remaining", remaining)
    // Consider refreshing the token
} else {
    fmt.Println("Token has expired")
}

// Use in middleware for early refresh
if claims.Timeleft() < 2*time.Minute {
    // Trigger token refresh process
    refreshToken()
}

type Expected added in v0.0.3

type Expected Claims // Separate type for conceptual clarity, same structure as Claims

Expected is a TokenValidator that performs exact-match validation of standard JWT claims.

It validates that the claims in a verified token exactly match the expected values. Only non-zero fields in the Expected struct are validated, allowing partial validation.

This validator is useful for:

  • Ensuring tokens come from a specific issuer
  • Validating audience claims for API access control
  • Checking that tokens have specific subjects or IDs
  • Enforcing exact timing constraints

Example:

expected := jwt.Expected{
    Issuer:   "my-auth-service",
    Audience: jwt.Audience{"api", "web"},
    Subject:  "user123",
}

verifiedToken, err := jwt.Verify(alg, key, token, expected)
if errors.Is(err, jwt.ErrExpected) {
    log.Printf("Token validation failed: %v", err)
}

func (Expected) ValidateToken added in v0.0.3

func (e Expected) ValidateToken(token []byte, c Claims, err error) error

ValidateToken implements the TokenValidator interface. It performs exact-match validation of standard claims against expected values.

The validation logic:

  1. If there's a previous validation error, return it unchanged
  2. For each non-zero field in Expected, compare with the corresponding claim
  3. Return ErrExpected with field details if any mismatch is found
  4. Return nil if all specified fields match

Only non-zero/non-empty fields in the Expected struct are validated, allowing flexible partial validation.

type HTTPClient added in v0.0.14

type HTTPClient interface {
	Get(string) (*http.Response, error)
}

HTTPClient is an interface for making HTTP requests to fetch JWKS.

This interface allows custom HTTP client implementations and enables mocking for testing. It abstracts the HTTP client dependency used by FetchJWKS, providing flexibility for different HTTP configurations.

**Standard Implementation**: The *http.Client from Go's standard library implements this interface and is the most common choice.

**Custom Implementations**: Can be created for:

  • Adding authentication headers
  • Implementing request/response logging
  • Adding retry logic with exponential backoff
  • Custom TLS configurations
  • Request metrics and monitoring
  • Rate limiting and circuit breaker patterns

**Testing**: Mock implementations allow unit testing of JWKS fetching without making actual HTTP requests.

Example custom implementation:

type LoggingClient struct {
    Client *http.Client
    Logger *log.Logger
}

func (c *LoggingClient) Get(url string) (*http.Response, error) {
    c.Logger.Printf("Fetching JWKS from: %s", url)
    resp, err := c.Client.Get(url)
    if err != nil {
        c.Logger.Printf("JWKS fetch failed: %v", err)
        return nil, err
    }
    c.Logger.Printf("JWKS fetch success: %d", resp.StatusCode)
    return resp, nil
}

Example mock for testing:

type MockHTTPClient struct {
    Response *http.Response
    Error    error
}

func (m *MockHTTPClient) Get(url string) (*http.Response, error) {
    return m.Response, m.Error
}

type HeaderValidator added in v0.0.14

type HeaderValidator func(alg string, headerDecoded []byte) (Alg, PublicKey, InjectFunc, error)

HeaderValidator defines a function type for custom JWT header validation logic.

This interface enables sophisticated header validation beyond simple algorithm checking, including multi-key scenarios, dynamic algorithm selection, and custom header fields. It's the foundation for advanced JWT verification patterns like JWKS integration and multi-tenant key management.

Function signature parameters:

  • alg: Expected algorithm name (empty string allows dynamic algorithm selection)
  • headerDecoded: Raw JSON bytes of the decoded JWT header

Return values:

  • Alg: Algorithm implementation to use for verification (nil uses the provided alg)
  • PublicKey: Public key for verification (nil uses the key passed to Verify)
  • InjectFunc: Optional payload decryption function (nil skips decryption)
  • error: Validation error (non-nil indicates header rejection)

Behavior and usage patterns:

  1. **Algorithm Validation**: When alg is provided, validate that the header contains the expected algorithm. When alg is empty, extract and return the algorithm from the header for dynamic selection.

  2. **Key Selection**: Return a non-nil PublicKey to override the key passed to the Verify function. This enables multi-key scenarios where the key is selected based on header content (e.g., "kid" field).

  3. **Payload Decryption**: Return a non-nil InjectFunc to enable automatic payload decryption using AES-GCM before signature verification.

  4. **Error Handling**: Return an error for any validation failure, including unknown algorithms, missing required fields, or security violations.

Common implementations:

  • CompareHeader: Default implementation for basic algorithm validation
  • Keys.ValidateHeader: Multi-key validation using Key ID from header
  • Custom validators for JWKS integration or tenant-specific logic

Example custom validator:

func MyHeaderValidator(alg string, headerDecoded []byte) (jwt.Alg, jwt.PublicKey, jwt.InjectFunc, error) {
    var header struct {
        Alg string `json:"alg"`
        Kid string `json:"kid"`
    }

    if err := json.Unmarshal(headerDecoded, &header); err != nil {
        return nil, nil, nil, err
    }

    // Validate algorithm matches expectation
    if alg != "" && header.Alg != alg {
        return nil, nil, nil, jwt.ErrTokenAlg
    }

    // Select key based on Key ID
    key := getKeyFromDatabase(header.Kid)
    if key == nil {
        return nil, nil, nil, jwt.ErrUnknownKid
    }

    // Return algorithm and key for verification
    return jwt.RS256, key.PublicKey, nil, nil
}

Security considerations:

  • Always validate algorithms to prevent algorithm substitution attacks
  • Ensure key selection logic is secure and cannot be manipulated
  • Validate all required header fields before accepting tokens
  • Consider rate limiting and caching for external key lookups
var CompareHeader HeaderValidator = compareHeader

CompareHeader is the global header validator used for JWT token verification.

This package-level variable defines the default behavior for validating JWT headers across all verification operations in the application. It implements the HeaderValidator interface and is responsible for algorithm validation and basic header consistency checking.

**Default Behavior**: The default implementation (compareHeader) performs:

  • Algorithm matching between expected and token header
  • Header structure validation (standard vs reversed field order)
  • Support for headers with and without "typ": "JWT" field
  • Fast byte-level comparison for known algorithm patterns

**Customization Use Cases**:

  • **Cross-platform compatibility**: Handle tokens from other JWT libraries
  • **Legacy token support**: Accept non-standard header formats
  • **Third-party integration**: Support tokens from external services
  • **Custom validation**: Add application-specific header validation logic

**Global vs Per-Token Validation**:

  • Modifying CompareHeader affects ALL token verification in the application
  • For per-token validation, use VerifyWithHeaderValidator instead
  • Consider thread safety when modifying this global variable

**Thread Safety**: Changes should be made during application initialization before concurrent verification operations begin.

Example customization:

// Custom validator for third-party tokens
jwt.CompareHeader = func(alg string, headerDecoded []byte) (jwt.Alg, jwt.PublicKey, jwt.InjectFunc, error) {
    // Parse header to extract algorithm
    var header struct {
        Alg string `json:"alg"`
        Typ string `json:"typ"`
        // Add custom fields as needed
    }

    if err := json.Unmarshal(headerDecoded, &header); err != nil {
        return nil, nil, nil, err
    }

    // Validate algorithm matches expectation
    if alg != "" && header.Alg != alg {
        return nil, nil, nil, jwt.ErrTokenAlg
    }

    // Accept both "JWT" and "jwt" type values (case insensitive)
    if header.Typ != "" && strings.ToUpper(header.Typ) != "JWT" {
        return nil, nil, nil, jwt.ErrTokenAlg
    }

    return nil, nil, nil, nil // Use provided algorithm and key
}

To restore default behavior: jwt.CompareHeader = jwt.compareHeader

type HeaderWithKid added in v0.0.14

type HeaderWithKid struct {
	Kid string `json:"kid"` // Key ID - identifies which key was used
	Alg string `json:"alg"` // Algorithm - cryptographic algorithm used
}

HeaderWithKid represents a JWT header containing Key ID and algorithm information. This structure is used for parsing and generating JWT headers when working with multiple keys.

The "kid" field identifies which key was used to sign the token, while "alg" specifies the cryptographic algorithm. Both fields are essential for proper multi-key JWT validation.

type InjectFunc added in v0.0.3

type InjectFunc func(plainPayload []byte) ([]byte, error)

InjectFunc defines a function type for modifying JWT payload data during token processing.

This function type enables payload transformation before signing (encoding) or after verification but before unmarshaling (decoding). It's the foundation for advanced JWT features like payload encryption, compression, and custom data transformations.

**Function Signature**:

  • Input: plainPayload []byte - The raw payload data to transform
  • Output: []byte - The transformed payload data
  • Output: error - Any transformation error

**Use Cases**:

  • **Encryption**: Encrypt sensitive payload data (see GCM function)
  • **Compression**: Compress large payloads to reduce token size
  • **Encoding**: Apply custom encoding schemes (base32, hex, etc.)
  • **Validation**: Add payload validation with transformation
  • **Filtering**: Remove or redact sensitive fields
  • **Augmentation**: Add computed fields or metadata

**Usage Contexts**:

  • **Signing**: Applied before payload is base64-encoded and signed
  • **Verification**: Applied after signature verification but before claims extraction
  • **Multi-key scenarios**: Different InjectFunc per key in Keys registry
  • **Custom algorithms**: Algorithm-specific payload processing

**Implementation Requirements**:

  • Must be deterministic for the same input (especially for signing)
  • Should be reversible if used for both encoding and decoding
  • Must handle edge cases gracefully (empty payloads, invalid data)
  • Should be thread-safe for concurrent operations
  • Error handling should be comprehensive and descriptive

**Built-in Implementations**:

  • GCM(): Creates encrypt/decrypt function pair for AES-GCM encryption
  • Custom implementations for specific transformation needs

Example implementations:

// Simple compression function
func compress(payload []byte) ([]byte, error) {
    var buf bytes.Buffer
    writer := gzip.NewWriter(&buf)
    if _, err := writer.Write(payload); err != nil {
        return nil, err
    }
    if err := writer.Close(); err != nil {
        return nil, err
    }
    return buf.Bytes(), nil
}

// Field filtering function
func filterSensitiveFields(payload []byte) ([]byte, error) {
    var claims map[string]any
    if err := json.Unmarshal(payload, &claims); err != nil {
        return nil, err
    }

    // Remove sensitive fields
    delete(claims, "password")
    delete(claims, "ssn")

    return json.Marshal(claims)
}

// Usage with signing
encryptFunc, decryptFunc, err := jwt.GCM(encryptionKey, nil)
if err != nil {
    return err
}

token, err := jwt.SignEncrypted(jwt.HS256, secretKey, encryptFunc, claims)

// Usage with verification (automatic with Keys)
keys := make(jwt.Keys)
keys["key1"] = &jwt.Key{
    Alg:     jwt.HS256,
    Private: secretKey,
    Public:  secretKey,
    Encrypt: encryptFunc,
    Decrypt: decryptFunc,
}

func GCM added in v0.0.2

func GCM(key, additionalData []byte) (encrypt, decrypt InjectFunc, err error)

GCM creates encrypt and decrypt functions for JWT payload encryption using AES-GCM (Galois/Counter Mode) authenticated encryption.

This function provides an additional layer of security by encrypting the JWT payload before signing. The encrypted payload is opaque to intermediate parties and provides both confidentiality and integrity protection.

Parameters:

  • key: AES encryption key, must be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256
  • additionalData: Optional authenticated data (AAD), can be nil

Returns two InjectFunc functions for encryption and decryption, or an error if the key size is invalid or cipher initialization fails.

The encryption function prepends a random nonce to the ciphertext. The decryption function extracts the nonce and authenticates the data.

Example:

// Generate keys
encKey := jwt.MustGenerateRandom(32)  // AES-256 key
sigKey := jwt.MustGenerateRandom(32)  // HMAC key

// Create encrypt/decrypt functions
encrypt, decrypt, err := jwt.GCM(encKey, nil)
if err != nil {
    log.Fatal(err)
}

// Sign with encryption
token, err := jwt.SignEncrypted(jwt.HS256, sigKey, encrypt, claims, jwt.MaxAge(15*time.Minute))

// Verify with decryption
verifiedToken, err := jwt.VerifyEncrypted(jwt.HS256, sigKey, decrypt, token)

type JWK added in v0.0.14

type JWK struct {
	Kty string `json:"kty"` // Key type: "RSA", "EC", "OKP"
	Kid string `json:"kid"` // Key ID for key selection
	Use string `json:"use"` // Key use: "sig" (signature), "enc" (encryption)
	Alg string `json:"alg"` // Algorithm: "RS256", "ES256", "EdDSA", etc.
	Crv string `json:"crv"` // Curve name: "P-256", "P-384", "P-521", "Ed25519"
	N   string `json:"n"`   // RSA modulus (Base64url-encoded)
	E   string `json:"e"`   // RSA exponent (Base64url-encoded)
	Y   string `json:"y"`   // EC y-coordinate (Base64url-encoded)
	X   string `json:"x"`   // EC x-coordinate or EdDSA key material (Base64url-encoded)
}

JWK represents a JSON Web Key as defined in RFC 7517.

A JSON Web Key (JWK) is a JSON data structure that represents a cryptographic key. JWKs are commonly used in OAuth 2.0, OpenID Connect, and other protocols for representing public keys used for signature verification.

**Key Components**:

  • kty: Key type (RSA, EC, OKP) - determines the key algorithm family
  • kid: Key ID - unique identifier for key selection
  • use: Key use (sig for signature, enc for encryption)
  • alg: Algorithm (RS256, ES256, EdDSA, etc.)
  • Key material: Algorithm-specific fields (n,e for RSA; x,y,crv for EC; x,crv for EdDSA)

**Supported Key Types**:

  • RSA (kty="RSA"): Uses n (modulus) and e (exponent) fields
  • ECDSA (kty="EC"): Uses x, y (coordinates) and crv (curve) fields
  • EdDSA (kty="OKP"): Uses x (key material) and crv (curve) fields

**Security**: JWKs contain public key material only. The corresponding private keys must be kept secure and are never included in JWKs.

**Usage Pattern**:

  1. Received as part of JWKS from identity provider
  2. Parsed into JWK struct via JSON unmarshaling
  3. Converted to Go crypto types using convertJWKToPublicKey
  4. Used for JWT signature verification

Example RSA JWK JSON:

{
  "kty": "RSA",
  "kid": "rsa-key-1",
  "use": "sig",
  "alg": "RS256",
  "n": "base64url-encoded-modulus",
  "e": "AQAB"
}

Example ECDSA JWK JSON:

{
  "kty": "EC",
  "kid": "ec-key-1",
  "use": "sig",
  "alg": "ES256",
  "crv": "P-256",
  "x": "base64url-x-coordinate",
  "y": "base64url-y-coordinate"
}

Example EdDSA JWK JSON:

{
  "kty": "OKP",
  "kid": "ed25519-key-1",
  "use": "sig",
  "alg": "EdDSA",
  "crv": "Ed25519",
  "x": "base64url-encoded-public-key"
}

func GenerateJWK added in v0.0.14

func GenerateJWK(kid string, alg string, publicKey PublicKey) (*JWK, error)

GenerateJWK creates a JSON Web Key (JWK) from a Go cryptographic public key.

This function converts Go's standard cryptographic public key types into the JWK format suitable for publishing in JWKS endpoints or sharing with other services for JWT verification. It's the reverse operation of convertJWKToPublicKey.

**Parameters**:

  • kid: Key ID string for identifying this key in key sets
  • alg: Algorithm string (e.g., "RS256", "ES256", "EdDSA")
  • publicKey: Go cryptographic public key interface

**Supported Key Types**:

  • *rsa.PublicKey: Converted to RSA JWK with "kty": "RSA"
  • ecdsa.PublicKey: Converted to ECDSA JWK with "kty": "EC"
  • ed25519.PublicKey: Converted to EdDSA JWK with "kty": "OKP"

**Algorithm Mapping**:

  • RSA: RS256, RS384, RS512, PS256, PS384, PS512
  • ECDSA: ES256 (P-256), ES384 (P-384), ES512 (P-521)
  • EdDSA: EdDSA (Ed25519)

**JWK Structure**: Generated JWKs include:

  • Common fields: kty, kid, use ("sig"), alg
  • RSA-specific: n (modulus), e (exponent)
  • ECDSA-specific: crv (curve), x, y (coordinates)
  • EdDSA-specific: crv ("Ed25519"), x (key material)

**Use Cases**:

  • Creating JWKS endpoints for token verification
  • Sharing public keys with other services
  • Key rotation and management systems
  • Converting keys from other formats to JWK
  • Testing and development with generated keys

**Security**: Only public key material is included in the JWK. Private keys should be kept secure and never included in JWKs.

Example usage:

// Generate RSA key pair
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
    return err
}

// Create JWK from public key
jwk, err := jwt.GenerateJWK("rsa-key-1", "RS256", &privateKey.PublicKey)
if err != nil {
    log.Printf("Failed to generate JWK: %v", err)
    return err
}

// Serialize to JSON
jwkJSON, err := json.Marshal(jwk)
if err != nil {
    return err
}

// Use in JWKS
jwks := jwt.JWKS{
    Keys: []*jwt.JWK{jwk},
}

// ECDSA example
ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
jwkEC, err := jwt.GenerateJWK("ec-key-1", "ES256", ecKey.PublicKey)

// EdDSA example
_, edKey, err := ed25519.GenerateKey(rand.Reader)
jwkEd, err := jwt.GenerateJWK("ed-key-1", "EdDSA", edKey)

**Error Handling**: Returns error for unsupported key types or invalid key parameters (e.g., unsupported elliptic curves).

type JWKS added in v0.0.14

type JWKS struct {
	Keys []*JWK `json:"keys"` // Array of JSON Web Keys
}

JWKS represents a JSON Web Key Set as defined in RFC 7517.

A JSON Web Key Set (JWKS) is a JSON structure that represents a set of JSON Web Keys (JWKs). It is commonly used by authorization servers and identity providers to publish their public keys for JWT signature verification.

**Structure**: The JWKS contains an array of JWK objects under the "keys" field. Each JWK represents a cryptographic key with metadata such as algorithm, key type, key ID, and the actual key material.

**Common Sources**:

  • OAuth 2.0 / OpenID Connect providers (/.well-known/jwks.json)
  • JWT issuers publishing verification keys
  • API gateways and authentication services
  • Identity providers (Auth0, AWS Cognito, Google, Microsoft)

**Usage Pattern**:

  1. Fetch JWKS from a trusted endpoint
  2. Parse into JWKS struct via JSON unmarshaling
  3. Convert to Keys map using PublicKeys() method
  4. Use Keys map for JWT verification with multiple key support

**Security**: JWKS should always be fetched over HTTPS from trusted sources. The keys contained within are public keys, but their integrity is critical for JWT security.

Example JWKS JSON structure:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "key1",
      "use": "sig",
      "alg": "RS256",
      "n": "base64url-encoded-modulus",
      "e": "AQAB"
    },
    {
      "kty": "EC",
      "kid": "key2",
      "use": "sig",
      "alg": "ES256",
      "crv": "P-256",
      "x": "base64url-x-coordinate",
      "y": "base64url-y-coordinate"
    }
  ]
}

func FetchJWKS added in v0.0.14

func FetchJWKS(client HTTPClient, url string) (*JWKS, error)

FetchJWKS fetches a JSON Web Key Set (JWKS) from the specified URL using a custom HTTP client.

This function provides fine-grained control over the HTTP request used to fetch JWKS, allowing custom timeouts, headers, authentication, and other HTTP client configurations. It's the lower-level function used by FetchPublicKeys.

**Parameters**:

  • client: HTTP client interface for making the request (nil uses http.DefaultClient)
  • url: The JWKS endpoint URL, typically ending with /.well-known/jwks.json

**HTTP Client Configuration**: The client parameter allows customization of:

  • Request timeouts and retry policies
  • Custom headers (User-Agent, Authorization, etc.)
  • Proxy and TLS configuration
  • Connection pooling and keep-alive settings
  • Request middleware and logging

**Response Handling**:

  • Success: HTTP status 200-399 with valid JSON JWKS
  • Error: HTTP status >= 400 returns httpError with status and body
  • Network errors: DNS, connection, timeout failures
  • JSON errors: Malformed JWKS response body

**Security Considerations**:

  • Always use HTTPS URLs in production
  • Set reasonable timeouts to prevent hanging requests
  • Validate the URL is from a trusted domain
  • Consider rate limiting for repeated requests
  • Use appropriate User-Agent headers

**Use Cases**:

  • Custom timeout requirements for slow identity providers
  • Adding authentication headers for private JWKS endpoints
  • Implementing request logging and metrics
  • Using custom TLS configurations
  • Adding retry logic with exponential backoff

Example usage:

// Custom client with timeout
client := &http.Client{
    Timeout: 10 * time.Second,
}
jwks, err := jwt.FetchJWKS(client, "https://auth.example.com/.well-known/jwks.json")
if err != nil {
    log.Printf("Failed to fetch JWKS: %v", err)
    return
}

// Convert to usable keys
keys := jwks.PublicKeys()

// Custom client with headers
type authenticatedClient struct {
    *http.Client
    apiKey string
}

func (c *authenticatedClient) Get(url string) (*http.Response, error) {
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Authorization", "Bearer "+c.apiKey)
    return c.Client.Do(req)
}

authClient := &authenticatedClient{
    Client: &http.Client{Timeout: 15 * time.Second},
    apiKey: "your-api-key",
}
jwks, err = jwt.FetchJWKS(authClient, privateJWKSURL)

**Error Handling**: Check for httpError to handle HTTP-specific errors:

jwks, err := jwt.FetchJWKS(client, url)
if err != nil {
    if httpErr, ok := err.(httpError); ok {
        log.Printf("HTTP error %d: %s", httpErr.StatusCode, httpErr.Body)
    }
    return err
}

func (*JWKS) PublicKeys added in v0.0.14

func (set *JWKS) PublicKeys() Keys

PublicKeys parses the JWKS and returns the public keys as a Keys map.

This method converts the JSON Web Key Set into a Keys map that can be used directly with JWT verification functions. It processes each JWK in the JWKS, validates the key format, and converts supported keys to their corresponding Go cryptographic types.

**Supported Key Types**:

  • RSA keys (kty: "RSA") -> *rsa.PublicKey for RS256/RS384/RS512/PS256/PS384/PS512
  • ECDSA keys (kty: "EC") -> *ecdsa.PublicKey for ES256/ES384/ES512
  • EdDSA keys (kty: "OKP") -> ed25519.PublicKey for Ed25519

**Key Filtering**: Only keys with:

  • Valid and supported key type (kty)
  • Recognized algorithm (alg)
  • Proper key material format
  • Valid base64url encoding

**Key ID Mapping**: The returned Keys map uses the JWK's "kid" (Key ID) field as the map key. This allows JWT verification to select the correct key based on the token's header "kid" claim.

**Error Handling**: Invalid or unsupported keys are silently skipped rather than causing the entire operation to fail. This allows JWKS with mixed key types to work partially.

**Return Value**: Keys map where:

  • Key: kid (Key ID) from JWK
  • Value: *Key struct containing ID, algorithm, and public key

Example usage:

// Parse JWKS from JSON
var jwks jwt.JWKS
err := json.Unmarshal(jwksJSON, &jwks)
if err != nil {
    log.Printf("Failed to parse JWKS: %v", err)
    return
}

// Convert to Keys map
keys := jwks.PublicKeys()
log.Printf("Found %d valid keys", len(keys))

// Inspect available keys
for keyID, key := range keys {
    log.Printf("Key %s: %s algorithm", keyID, key.Alg)
}

// Use for JWT verification
verifiedToken, err := jwt.Verify(jwt.RS256, keys, tokenBytes)

// Keys can also be used with specific algorithms
if rsaKey, exists := keys["my-rsa-key"]; exists {
    verifiedToken, err := jwt.Verify(jwt.RS256, rsaKey.Public, tokenBytes)
}

**Performance**: This method creates new Key structs and converts cryptographic keys, so consider caching the result for repeated use.

type Key added in v0.0.14

type Key struct {
	ID      string        // Unique identifier for this key
	Alg     Alg           // Algorithm implementation for this key
	Public  PublicKey     // Public key for verification (can be nil for HMAC)
	Private PrivateKey    // Private key for signing (required for signing)
	MaxAge  time.Duration // Optional: automatic token expiration
	Encrypt InjectFunc    // Optional: payload encryption function
	Decrypt InjectFunc    // Optional: payload decryption function
}

Key represents a complete cryptographic key configuration for JWT operations. It combines algorithm specification, key material, and optional features like expiration and encryption into a single manageable unit.

This structure supports:

  • Asymmetric keys (RSA, ECDSA, EdDSA) with separate public/private components
  • Symmetric keys (HMAC) where public and private are the same
  • Automatic token expiration via MaxAge
  • Payload encryption/decryption for enhanced security

Use the package-level parsing functions like ParsePublicKeyRSA, ParsePrivateKeyRSA, etc., to populate the Public and Private fields from PEM data.

func (*Key) Configuration added in v0.1.15

func (key *Key) Configuration() (KeyConfiguration, error)

Configuration converts a Key instance to a KeyConfiguration for serialization.

This method extracts the key material and converts it to PEM-encoded strings suitable for storage in configuration files. It's useful for:

  • Exporting key configurations for backup
  • Converting runtime keys to persistent storage format
  • Creating templates for deployment configurations

Returns an error if:

  • The key includes encryption functions (not serializable)
  • The key material cannot be encoded to PEM format
  • The key type is not supported for encoding

Example:

key := &jwt.Key{
    ID: "my-key",
    Alg: jwt.RS256,
    Private: privateKey,
    Public: publicKey,
    MaxAge: 1 * time.Hour,
}

config, err := key.Configuration()
// config can now be marshaled to JSON/YAML/etc.

type KeyConfiguration added in v0.0.14

type KeyConfiguration struct {
	ID string `json:"id" yaml:"ID" toml:"ID" ini:"id"`
	// Alg declares the algorithm name.
	// Available values:
	//  * HS256
	//  * HS384
	//  * HS512
	//  * RS256
	//  * RS384
	//  * RS512
	//  * PS256
	//  * PS384
	//  * PS512
	//  * ES256
	//  * ES384
	//  * ES512
	//  * EdDSA
	Alg     string `json:"alg" yaml:"Alg" toml:"Alg" ini:"alg"`
	Private string `json:"private" yaml:"Private" toml:"Private" ini:"private"`
	Public  string `json:"public" yaml:"Public" toml:"Public" ini:"public"`
	// MaxAge sets the token expiration. It is optional.
	// If greater than zero then the MaxAge token validation
	// will be appended to the "VerifyToken" and the token is invalid
	// after expiration of its sign time.
	MaxAge time.Duration `json:"max_age" yaml:"MaxAge" toml:"MaxAge" ini:"max_age"`

	// EncryptionKey enables encryption on the generated token. It is optional.
	// Encryption using the Galois Counter mode of operation with
	// AES cipher symmetric-key cryptographic.
	//
	// It should be HEX-encoded string value.
	//
	// The value should be the AES key,
	// either 16, 24, or 32 bytes to select
	// AES-128, AES-192, or AES-256.
	EncryptionKey string `json:"encryption_key" yaml:"EncryptionKey" toml:"EncryptionKey" ini:"encryption_key"`
}

KeyConfiguration defines a single key's configuration in serializable format.

This structure uses string fields to represent all key data, making it suitable for JSON/YAML/TOML/INI serialization. The string-based key fields support both PEM-encoded key data and quoted strings for flexibility.

The EncryptionKey field enables payload encryption using AES-GCM when specified. The key should be hex-encoded and of appropriate length (16, 24, or 32 bytes for AES-128, AES-192, or AES-256 respectively).

func (KeyConfiguration) Clone added in v0.0.14

Clone creates a deep copy of the KeyConfiguration.

This method returns a new KeyConfiguration with all string fields copied. Since all fields are strings or basic types, this is equivalent to a deep copy. This is useful for creating variations of configurations or ensuring isolation when passing configurations between goroutines.

Example:

original := KeyConfiguration{ID: "key1", Alg: "RS256"}
copy := original.Clone()
copy.ID = "key2" // Doesn't affect original

type Keys added in v0.0.14

type Keys map[string]*Key

Keys is a thread-safe registry of cryptographic keys indexed by Key ID.

This map-based structure allows applications to manage multiple keys simultaneously, which is essential for:

  • Key rotation without service interruption
  • Supporting multiple token issuers
  • Separating keys by purpose (API access, refresh tokens, etc.)
  • Integration with external key sources (JWKS, AWS Cognito, etc.)

The Keys type implements HeaderValidator, making it directly usable with VerifyWithHeaderValidator for automatic key selection based on token headers.

IMPORTANT: Keys is NOT safe for concurrent modification. Initialize all keys before starting concurrent operations, or use external synchronization.

Example:

keys := make(jwt.Keys)
keys.Register(jwt.RS256, "api-key", publicKey, privateKey)
keys.Register(jwt.ES256, "backup-key", backupPublic, backupPrivate)

// Signing automatically includes "kid" header
token, err := keys.SignToken("api-key", claims, jwt.MaxAge(15*time.Minute))

// Verification automatically selects key based on "kid" header
var claims MyClaims
err = keys.VerifyToken(token, &claims)

// Generate JWKS for external consumption
jwks, err := keys.JWKS()

func FetchAWSCognitoPublicKeys added in v0.0.14

func FetchAWSCognitoPublicKeys(region, userPoolID string) (Keys, error)

FetchAWSCognitoPublicKeys fetches the JSON Web Key Set (JWKS) from the AWS Cognito endpoint and returns the public keys as Keys map. It returns an error if the request fails or the JWKS is invalid.

func FetchPublicKeys added in v0.0.14

func FetchPublicKeys(url string) (Keys, error)

FetchPublicKeys fetches a JSON Web Key Set (JWKS) from the specified URL and returns the public keys as a Keys map for JWT verification.

This is a convenience function that combines FetchJWKS and JWKS.PublicKeys() operations. It uses the default HTTP client and is suitable for most standard JWKS fetching scenarios.

**Parameters**:

  • url: The JWKS endpoint URL, typically ending with /.well-known/jwks.json

**Supported Algorithms**:

  • RSA: RS256, RS384, RS512, PS256, PS384, PS512
  • ECDSA: ES256 (P-256), ES384 (P-384), ES512 (P-521)
  • EdDSA: Ed25519 curve

**Common JWKS Endpoints**:

**Error Handling**: Returns error if:

  • Network request fails (DNS, connection, timeout)
  • HTTP response status >= 400
  • Invalid JSON in JWKS response
  • No valid keys found in the JWKS

**Security Considerations**:

  • Always use HTTPS URLs to prevent man-in-the-middle attacks
  • Consider implementing caching and rate limiting for production use
  • Validate that URLs are from trusted domains
  • Keys are automatically filtered - only supported algorithms are included

Example usage:

// Fetch keys from Auth0
keys, err := jwt.FetchPublicKeys("https://myapp.auth0.com/.well-known/jwks.json")
if err != nil {
    log.Printf("Failed to fetch keys: %v", err)
    return
}

// Use keys for verification with kid
verifiedToken, err := jwt.Verify(jwt.RS256, keys, tokenBytes)
if err != nil {
    log.Printf("Verification failed: %v", err)
    return
}

// Keys map allows multiple algorithms
for keyID, key := range keys {
    log.Printf("Key ID: %s, Algorithm: %s", keyID, key.Alg)
}

**Advanced Usage**: For custom HTTP clients, timeouts, or headers, use FetchJWKS directly instead of this convenience function.

func (Keys) Configuration added in v0.1.15

func (keys Keys) Configuration() (KeysConfiguration, error)

Configuration converts a Keys registry to a KeysConfiguration for serialization.

This method processes all keys in the registry and converts them to their serializable KeyConfiguration representations. It's particularly useful for:

  • Creating configuration backups
  • Exporting keys loaded from JWKS endpoints
  • Converting runtime key registries to file-based configurations
  • Template generation for deployment automation

Returns an error if any key in the registry:

  • Includes encryption functions (not serializable)
  • Contains key material that cannot be encoded to PEM
  • Uses unsupported key types

This method is often used in conjunction with JWKS.PublicKeys() to convert downloaded public keys into persistent configuration format.

Example:

// Load keys from JWKS endpoint
keys, err := jwt.FetchPublicKeys("https://auth.example.com/.well-known/jwks.json")

// Convert to configuration for local storage
config, err := keys.Configuration()

// Save to configuration file
data, _ := json.MarshalIndent(config, "", "  ")
os.WriteFile("keys.json", data, 0644)

func (Keys) EnrichToken added in v0.1.16

func (keys Keys) EnrichToken(plainToken []byte, extraClaims any) ([]byte, error)

EnrichToken creates a new JWT token by merging the original token's claims with additional claims.

This method allows you to extend the original token's payload with new claims while preserving the original token's header and signature structure.

It uses the same algorithm and key as the original token to ensure the new token is valid.

Parameters:
 - key: PrivateKey used to sign the new token
 - extraClaims: Map of additional claims to merge with the original token's payload

Returns:

  • []byte: New JWT token with merged claims
  • error: Error if the original token's algorithm cannot be determined or if merging fails.

Note: this only enrich plain tokens and not encrypted tokens.

func (Keys) Get added in v0.0.14

func (keys Keys) Get(kid string) (*Key, bool)

Get retrieves a Key from the registry by its Key ID.

This method provides direct access to registered keys using their unique identifiers. It's the fundamental lookup mechanism used internally by SignToken, VerifyToken, and ValidateHeader methods.

Parameters:

  • kid: The Key ID to look up

Returns:

  • *Key: The key if found, nil if not found
  • bool: true if the key exists, false otherwise

The method is safe for concurrent read access, but the Keys registry itself is not safe for concurrent modification. Ensure all keys are registered before starting concurrent operations.

Example:

keys := make(jwt.Keys)
keys.Register(jwt.RS256, "api-key", publicKey, privateKey)

key, exists := keys.Get("api-key")
if exists {
    fmt.Printf("Found key: %s algorithm\n", key.Alg.Name())
} else {
    fmt.Println("Key not found")
}

func (Keys) JWKS added in v0.0.14

func (keys Keys) JWKS() (*JWKS, error)

JWKS generates a JSON Web Key Set (JWKS) from all registered public keys.

This method creates a JWKS structure containing the public keys from all registered keys in the registry. The resulting JWKS is ready for serving at the standard /.well-known/jwks.json endpoint to enable external services to verify tokens signed by this application.

The JWKS generation process:

  • Iterates through all registered keys in the registry
  • Extracts public key material from each key
  • Converts keys to JWK (JSON Web Key) format with appropriate parameters
  • Includes algorithm and key ID information for each key
  • Assembles all JWKs into a JWKS structure

Returns:

  • *JWKS: Complete JSON Web Key Set ready for serialization
  • error: Key conversion errors if any key cannot be processed

The generated JWKS follows RFC 7517 specifications and includes:

  • "kty" (Key Type): RSA, EC, or OKP depending on the algorithm
  • "kid" (Key ID): Unique identifier for each key
  • "alg" (Algorithm): The intended algorithm for the key
  • Key-specific parameters (n, e for RSA; x, y, crv for ECDSA; etc.)

Only public keys are included in the JWKS for security reasons. Private keys are never exposed through this interface.

Example:

keys := make(jwt.Keys)
keys.Register(jwt.RS256, "rsa-key", rsaPublic, rsaPrivate)
keys.Register(jwt.ES256, "ec-key", ecdsaPublic, ecdsaPrivate)

jwks, err := keys.JWKS()
if err != nil {
    log.Fatalf("Failed to generate JWKS: %v", err)
}

// Serve the JWKS at /.well-known/jwks.json
http.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(jwks)
})

See RFC 7517 (https://tools.ietf.org/html/rfc7517) for complete JWKS specifications.

func (Keys) Register added in v0.0.14

func (keys Keys) Register(alg Alg, kid string, pubKey PublicKey, privKey PrivateKey)

Register adds a cryptographic key pair to the Keys registry with a unique identifier.

This method creates a new Key entry in the registry, making it available for token signing and verification operations. The registered key will be used automatically when its Key ID is referenced in JWT headers.

Parameters:

  • alg: The cryptographic algorithm implementation to use with this key
  • kid: Unique Key ID string that will identify this key in JWT headers
  • pubKey: Public key for token verification (can be nil for HMAC algorithms)
  • privKey: Private key for token signing (required for signing operations)

The method overwrites any existing key with the same ID without warning. This behavior supports key rotation scenarios where old keys are replaced with new ones using the same identifier.

IMPORTANT: This method is NOT safe for concurrent use. Register all keys during application initialization before starting concurrent operations, or use external synchronization if runtime registration is required.

Example:

keys := make(jwt.Keys)

// Register RSA key pair
keys.Register(jwt.RS256, "rsa-key", &rsaPrivateKey.PublicKey, rsaPrivateKey)

// Register ECDSA key pair
keys.Register(jwt.ES256, "ecdsa-key", &ecdsaPrivateKey.PublicKey, ecdsaPrivateKey)

// Register HMAC key (public key not needed)
keys.Register(jwt.HS256, "hmac-key", nil, hmacSecret)

// Now keys can be used for signing and verification
token, err := keys.SignToken("rsa-key", claims)

func (Keys) SignToken added in v0.0.14

func (keys Keys) SignToken(kid string, claims any, opts ...SignOption) ([]byte, error)

SignToken creates and signs a JWT using the specified key from the registry.

This method provides a high-level interface for JWT creation with multi-key support. It automatically includes the Key ID in the JWT header and applies any configured key-specific options like MaxAge. The resulting token can be verified using VerifyToken or any JWT library that supports the same algorithm.

The method performs the following operations:

  • Looks up the key by its ID in the registry
  • Applies key-specific MaxAge if configured
  • Includes "kid" and "alg" fields in the JWT header
  • Signs the token using the key's algorithm and private key
  • Applies optional payload encryption if configured

Parameters:

  • kid: Key ID identifying which registered key to use for signing
  • claims: The claims to include in the JWT payload (any JSON-serializable type)
  • opts: Optional signing options (MaxAge, custom headers, etc.)

Returns:

  • []byte: The complete JWT token as bytes
  • error: ErrUnknownKid if the key ID is not registered, or signing errors

The SignOptions are applied in addition to any key-specific options. If both the key and the options specify MaxAge, the key's MaxAge takes precedence by being applied first.

Example:

keys := make(jwt.Keys)
keys.Register(jwt.RS256, "api-key", publicKey, privateKey)

claims := jwt.Map{"sub": "user123", "role": "admin"}
token, err := keys.SignToken("api-key", claims, jwt.MaxAge(15*time.Minute))
if err != nil {
    log.Fatalf("Failed to sign token: %v", err)
}

// Token header will include: {"kid":"api-key","alg":"RS256"}
fmt.Printf("Token: %s\n", token)

func (Keys) ValidateHeader added in v0.0.14

func (keys Keys) ValidateHeader(alg string, headerDecoded []byte) (Alg, PublicKey, InjectFunc, error)

ValidateHeader validates JWT header and selects the appropriate key for verification.

This method implements the HeaderValidator interface, enabling automatic key selection based on the "kid" (Key ID) field in JWT headers. It performs comprehensive validation of the token header and returns the cryptographic components needed for verification.

The validation process includes:

  • JSON unmarshaling of the decoded header
  • Presence check for required "kid" field
  • Key lookup in the registry using the Key ID
  • Algorithm consistency verification between header and key
  • Optional algorithm parameter validation

Parameters:

  • alg: Optional algorithm constraint (empty string means no constraint)
  • headerDecoded: Base64-decoded JWT header JSON data

Returns:

  • Alg: The algorithm implementation for this key
  • PublicKey: The public key for signature verification
  • InjectFunc: Optional decryption function (nil if not configured)
  • error: Various validation errors (see below)

Possible errors:

  • JSON unmarshal errors if header is malformed
  • ErrEmptyKid if "kid" field is missing or empty
  • ErrUnknownKid if the Key ID is not registered
  • ErrTokenAlg if algorithms don't match

This method is used internally by VerifyToken and VerifyWithHeaderValidator to enable automatic key selection for multi-key JWT verification.

Example usage (typically internal):

// This is usually called automatically by VerifyToken
alg, pubKey, decrypt, err := keys.ValidateHeader("", headerBytes)
if err != nil {
    return fmt.Errorf("header validation failed: %v", err)
}

func (Keys) VerifyToken added in v0.0.14

func (keys Keys) VerifyToken(token []byte, claimsPtr any, validators ...TokenValidator) error

VerifyToken verifies a JWT token using automatic key selection and extracts claims.

This method provides a high-level interface for JWT verification with multi-key support. It automatically selects the appropriate key based on the "kid" header field and performs comprehensive token validation including signature verification and claims validation.

The verification process includes:

  • JWT header parsing and validation
  • Automatic key selection based on "kid" field
  • Algorithm consistency checking
  • Signature verification using the selected public key
  • Optional payload decryption if configured
  • Standard claims validation (exp, nbf, iat, etc.)
  • Custom claims extraction to the provided destination

Parameters:

  • token: The JWT token bytes to verify
  • claimsPtr: Pointer to a structure where verified claims will be unmarshaled
  • validators: Optional token validators for additional validation logic

Returns:

  • error: Various validation errors including:
  • ErrEmptyKid if the token lacks a "kid" header
  • ErrUnknownKid if the key ID is not registered
  • ErrTokenAlg if algorithms don't match
  • Signature verification errors
  • Claims validation errors
  • JSON unmarshaling errors for claims

The method supports any claims structure that can be JSON-unmarshaled. Use jwt.Map for dynamic claims or custom structs for typed claims.

Example:

keys := make(jwt.Keys)
keys.Register(jwt.RS256, "api-key", publicKey, privateKey)

// Verify with custom claims struct
type MyClaims struct {
    Sub  string `json:"sub"`
    Role string `json:"role"`
    jwt.RegisteredClaims
}

var claims MyClaims
err := keys.VerifyToken(token, &claims)
if err != nil {
    log.Fatalf("Token verification failed: %v", err)
}

fmt.Printf("User: %s, Role: %s\n", claims.Sub, claims.Role)

// Or verify with dynamic claims
var dynamicClaims jwt.Map
err = keys.VerifyToken(token, &dynamicClaims)

type KeysConfiguration added in v0.0.14

type KeysConfiguration []KeyConfiguration

KeysConfiguration represents a serializable configuration for multiple JWT keys.

This slice-based structure is designed for loading key configurations from external sources like JSON, YAML, TOML, or INI files. It provides a declarative way to define multiple keys with their algorithms, key material, and options.

Each KeyConfiguration is converted to a Key during the Load() process, which parses the string-based key data into actual cryptographic objects.

See _examples/multiple-kids for a complete usage example.

Example JSON configuration:

[
  {
    "id": "api-key",
    "alg": "RS256",
    "private": "-----BEGIN RSA PRIVATE KEY-----\n...",
    "public": "-----BEGIN PUBLIC KEY-----\n...",
    "max_age": "15m"
  }
]

func (KeysConfiguration) Clone added in v0.0.14

Clone creates a deep copy of the KeysConfiguration.

This method returns a new slice with cloned copies of all KeyConfiguration entries. The cloned configuration is independent and can be modified without affecting the original. However, the cloned configuration must still be processed with Load() or MustLoad() to parse the key material.

This is useful for:

  • Creating environment-specific configuration variants
  • Template-based configuration generation
  • Safe concurrent access to configuration data

Example:

original := KeysConfiguration{...}
development := original.Clone()
development[0].EncryptionKey = "dev-encryption-key"

devKeys := development.MustLoad()

func (KeysConfiguration) Get added in v0.0.14

Get retrieves a KeyConfiguration by its ID from the configuration slice.

This method performs a linear search through the configuration slice to find a KeyConfiguration with the matching ID. It returns the configuration and a boolean indicating whether the key was found.

Example:

config := KeysConfiguration{...}
keyConfig, found := config.Get("api-key")
if found {
    fmt.Printf("Found key: %s", keyConfig.Alg)
}

func (KeysConfiguration) Load added in v0.0.14

func (c KeysConfiguration) Load() (Keys, error)

Load parses the KeysConfiguration into a usable Keys registry.

This method processes each KeyConfiguration entry and converts string-based key data into actual cryptographic objects. The parsing process includes:

  • Algorithm name resolution to Alg implementations
  • PEM decoding and cryptographic key parsing
  • Encryption key processing (hex decoding and GCM setup)
  • Key validation and error checking

The method supports configuration loaded from JSON, YAML, TOML, or INI files. String fields may be quoted and will be automatically unquoted during processing.

Returns an error if:

  • Any algorithm name is not recognized
  • Key material cannot be parsed (invalid PEM, wrong format, etc.)
  • Encryption keys are invalid (wrong length, invalid hex, etc.)
  • GCM cipher initialization fails

Example:

// Load from JSON file
var config KeysConfiguration
data, _ := os.ReadFile("keys.json")
json.Unmarshal(data, &config)

keys, err := config.Load()
if err != nil {
    log.Fatalf("Failed to load keys: %v", err)
}

func (KeysConfiguration) MustLoad added in v0.0.14

func (c KeysConfiguration) MustLoad() Keys

MustLoad parses the KeysConfiguration into a Keys registry, panicking on error.

This is a convenience wrapper around Load() that panics if any error occurs during key parsing or configuration processing. Use this when you want to fail fast during application startup if key configuration is invalid.

Only use MustLoad when:

  • Configuration is known to be valid (e.g., embedded keys)
  • Application cannot function without valid keys
  • You want immediate failure during startup rather than runtime errors

For production applications with external configuration, prefer Load() for proper error handling.

Example:

// Embedded configuration that should always be valid
config := KeysConfiguration{...}
keys := config.MustLoad() // Panics if invalid

type Map

type Map = map[string]any

Map is a convenient type alias for map[string]any, commonly used for dynamic JWT claims.

This type provides a flexible way to work with JWT payloads that contain dynamic or unknown claim structures. It's particularly useful when:

  • Working with tokens from external sources with varying claim structures
  • Building generic JWT processing utilities
  • Handling tokens where claim types are determined at runtime
  • Prototyping and development scenarios

While Map provides flexibility, consider using typed structs for production applications where claim structure is known and type safety is important.

Example usage:

// Create claims dynamically
claims := jwt.Map{
    "sub":      "user123",
    "role":     "admin",
    "permissions": []string{"read", "write"},
    "iat":      time.Now().Unix(),
    "exp":      time.Now().Add(time.Hour).Unix(),
}

token, err := jwt.Sign(jwt.HS256, secretKey, claims)

// Verify and extract claims
var verifiedClaims jwt.Map
err = jwt.Verify(jwt.HS256, secretKey, token, &verifiedClaims)

// Access claims dynamically
userID := verifiedClaims["sub"].(string)
role := verifiedClaims["role"].(string)
permissions := verifiedClaims["permissions"].([]any)

type PrivateKey

type PrivateKey = any

PrivateKey represents any private key type used for JWT token signing operations.

This generic type alias allows the JWT library to work with various cryptographic private key types without requiring specific type assertions in user code. The actual key type depends on the algorithm being used:

Supported private key types by algorithm:

  • HMAC (HS256/384/512): []byte (shared secret)
  • RSA (RS256/384/512, PS256/384/512): *rsa.PrivateKey
  • ECDSA (ES256/384/512): *ecdsa.PrivateKey
  • EdDSA: ed25519.PrivateKey

The key must match the algorithm being used for signing, otherwise the Sign operation will fail with type assertion errors.

Example usage:

var hmacKey PrivateKey = []byte("secret-key")
var rsaKey PrivateKey = rsaPrivateKey
var ecKey PrivateKey = ecdsaPrivateKey

type PublicKey

type PublicKey = any

PublicKey represents any public key type used for JWT token verification operations.

This generic type alias allows the JWT library to work with various cryptographic public key types without requiring specific type assertions in user code. The actual key type depends on the algorithm being used:

Supported public key types by algorithm:

  • HMAC (HS256/384/512): []byte (same shared secret as private key)
  • RSA (RS256/384/512, PS256/384/512): *rsa.PublicKey
  • ECDSA (ES256/384/512): *ecdsa.PublicKey
  • EdDSA: ed25519.PublicKey

For symmetric algorithms (HMAC), the public key is the same as the private key. For asymmetric algorithms, use the corresponding public key extracted from the private key or loaded from external sources.

Example usage:

var hmacKey PublicKey = []byte("secret-key")      // Same as private
var rsaKey PublicKey = &rsaPrivateKey.PublicKey   // Extract from private
var ecKey PublicKey = &ecdsaPrivateKey.PublicKey  // Extract from private

type SignOption

type SignOption interface {
	// ApplyClaims applies standard JWT claims to the destination Claims struct.
	//
	// This method is called during token signing to set standard claims
	// based on the SignOption's configuration. Implementations should
	// modify the destination Claims struct to include their claim values.
	//
	// **Parameters**:
	//   - dest: Pointer to Claims struct to modify with standard claims
	//
	// **Implementation Notes**:
	//   - Should only set meaningful (non-zero) values unless explicitly clearing
	//   - Must handle nil destination gracefully (though framework prevents this)
	//   - Should be idempotent and free of side effects
	//   - Can override existing values in destination if appropriate
	//
	// **Threading**: Implementations should be thread-safe if the SignOption
	// instance will be shared across goroutines.
	ApplyClaims(*Claims)
}

SignOption provides a flexible interface for applying standard JWT claims during token signing.

This interface enables a composable approach to token creation by allowing various types to implement standard claim application logic. SignOptions are processed during token signing to automatically set common JWT claims without manual management.

**Interface Contract**: Implementers must provide an ApplyClaims method that modifies the provided Claims struct to set appropriate standard claim values. The method should be idempotent and handle nil or zero values gracefully.

**Built-in Implementations**:

  • MaxAge(duration): Sets exp and iat claims for token expiration
  • NoMaxAge: Removes exp and iat claims (no expiration)
  • Audience: Sets aud claim for intended recipients
  • Claims: Applies any standard claims from a Claims struct
  • SignOptionFunc: Function wrapper for custom claim logic

**Usage Pattern**: SignOptions are passed as variadic parameters to Sign functions and are processed in order. Later options can override earlier ones if they set the same claims, enabling override patterns.

**Design Benefits**:

  • Composable claim management
  • Type-safe standard claim application
  • Reusable claim configuration
  • Clean separation of concerns between custom and standard claims

**Implementation Guidelines**:

  • Only modify non-zero fields unless explicitly clearing values
  • Handle concurrent access if the implementation will be shared
  • Avoid side effects beyond modifying the provided Claims struct
  • Document which specific claims are modified by the implementation

Example implementations:

// Custom SignOption for organization claims
type OrgClaims struct {
    OrgID   string
    OrgName string
}

func (o OrgClaims) ApplyClaims(dest *jwt.Claims) {
    dest.Issuer = o.OrgName
    dest.Subject = o.OrgID
}

// Usage with custom SignOption
orgClaims := OrgClaims{OrgID: "org123", OrgName: "MyCompany"}
token, err := jwt.Sign(jwt.HS256, key, userClaims,
    orgClaims,
    jwt.MaxAge(time.Hour))

// Function-based SignOption
tenantOption := jwt.SignOptionFunc(func(c *jwt.Claims) {
    c.Issuer = "tenant-" + tenantID
    c.Audience = jwt.Audience{tenantID + "-api"}
})

token, err := jwt.Sign(jwt.HS256, key, claims, tenantOption)

Available built-in SignOptions:

  • MaxAge(time.Duration): Token expiration management
  • NoMaxAge: Removes expiration constraints
  • Audience{"service1", "service2"}: Intended recipients
  • Claims{Issuer: "app", Subject: "user"}: Standard claim values

type SignOptionFunc

type SignOptionFunc func(*Claims)

SignOptionFunc is a function type that implements the SignOption interface.

This type provides a convenient way to create SignOption implementations using function literals or existing functions, eliminating the need to define new types for simple claim application logic.

**Function Signature**: The function receives a pointer to a Claims struct and should modify it to apply the desired standard claims. The function should follow the same guidelines as other SignOption implementations.

**Use Cases**:

  • Quick inline SignOption creation with function literals
  • Converting existing claim-setting functions to SignOptions
  • Dynamic claim logic based on runtime conditions
  • Conditional claim application

**Benefits**:

  • Reduces boilerplate for simple SignOption implementations
  • Enables functional programming patterns for claim management
  • Allows capturing closure variables for dynamic behavior
  • Facilitates testing with mock claim functions

Example usage:

// Inline function literal
dynamicIssuer := jwt.SignOptionFunc(func(c *jwt.Claims) {
    c.Issuer = "app-" + os.Getenv("ENVIRONMENT")
    c.Subject = getCurrentUserID()
})

token, err := jwt.Sign(jwt.HS256, key, userClaims, dynamicIssuer)

// Conditional claim application
conditionalClaims := jwt.SignOptionFunc(func(c *jwt.Claims) {
    if isProduction {
        c.Issuer = "prod.myapp.com"
    } else {
        c.Issuer = "dev.myapp.com"
    }
})

// Converting existing function
setOrgClaims := func(c *jwt.Claims) {
    c.Issuer = organization.Name
    c.Subject = organization.ID
}
orgOption := jwt.SignOptionFunc(setOrgClaims)

// Multiple conditional options
getUserRole := jwt.SignOptionFunc(func(c *jwt.Claims) {
    switch user.Role {
    case "admin":
        c.Audience = jwt.Audience{"admin-api", "user-api"}
    case "user":
        c.Audience = jwt.Audience{"user-api"}
    default:
        c.Audience = jwt.Audience{"public-api"}
    }
})

token, err := jwt.Sign(jwt.HS256, key, userClaims,
    getUserRole,
    jwt.MaxAge(time.Hour))
var NoMaxAge SignOptionFunc = func(c *Claims) {
	c.Expiry = 0
	c.IssuedAt = 0
}

NoMaxAge is a SignOption that removes expiration constraints from JWT tokens.

This SignOptionFunc sets both the "exp" (expiry) and "iat" (issued at) claims to zero, effectively creating tokens without time-based expiration. This is useful for long-lived tokens, permanent API keys, or testing scenarios.

**Security Warning**: Tokens without expiration pose security risks as they remain valid indefinitely if compromised. Use only when absolutely necessary and implement alternative revocation mechanisms.

**Claims Modified**:

  • Expiry (exp): Set to 0 (no expiration)
  • IssuedAt (iat): Set to 0 (no issue time tracking)

**Use Cases**:

  • Permanent API keys for system-to-system communication
  • Long-lived refresh tokens (with alternative revocation)
  • Development and testing environments
  • Legacy system integration where expiration isn't supported

**Alternative Approaches**: Consider implementing:

  • Very long durations instead of no expiration
  • Token rotation policies
  • Manual revocation systems
  • Refresh token patterns with shorter access tokens

Example usage:

// Create token without expiration
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims, jwt.NoMaxAge)

// Override MaxAge with NoMaxAge
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.MaxAge(time.Hour),    // This would set expiration
    jwt.NoMaxAge)             // This removes expiration

// Use with other options
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.NoMaxAge,
    jwt.Audience{"api-service"},
    jwt.Claims{Issuer: "system"})

**Security Best Practice**: When using NoMaxAge, implement alternative security measures such as token blacklisting, regular key rotation, or application-level session management.

func MaxAge

func MaxAge(maxAge time.Duration) SignOptionFunc

MaxAge creates a SignOption to set expiration and issued-at claims for JWT tokens.

This function generates a SignOptionFunc that automatically sets both the "exp" (expiry) and "iat" (issued at) claims based on the current time and specified duration. It provides a convenient way to create tokens with consistent lifetimes.

**Parameters**:

  • maxAge: Duration the token should remain valid from issuance time

**Behavior**:

  • If maxAge <= 1 second: Returns NoMaxAge (removes expiration)
  • If maxAge > 1 second: Sets expiry to current time + maxAge
  • Always sets IssuedAt to current time when expiry is set

**Claims Set**:

  • Expiry (exp): Current time + maxAge duration (Unix timestamp)
  • IssuedAt (iat): Current time (Unix timestamp)

**Time Source**: Uses the configurable Clock() function for current time, allowing consistent time handling and testing flexibility.

**Usage Patterns**:

  • Standard token lifetimes: 15 minutes, 1 hour, 24 hours
  • Session tokens with auto-expiry
  • Short-lived tokens for sensitive operations
  • API tokens with controlled access windows

**Security Benefits**:

  • Prevents indefinite token usage
  • Enables automatic token expiration
  • Reduces risk of token compromise over time
  • Facilitates token rotation policies

Example usage:

// 15-minute token for API access
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.MaxAge(15 * time.Minute))

// 1-hour token for web sessions
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.MaxAge(time.Hour))

// Combine with other options
token, err := jwt.Sign(jwt.HS256, secretKey, userClaims,
    jwt.MaxAge(30 * time.Minute),
    jwt.Audience{"api-service"},
    jwt.Claims{Issuer: "myapp.com"})

// Very short duration returns NoMaxAge
signOption := jwt.MaxAge(500 * time.Millisecond) // Returns NoMaxAge

See the Clock package-level variable to customize the current time function for testing or specific timezone requirements.

func (SignOptionFunc) ApplyClaims

func (f SignOptionFunc) ApplyClaims(c *Claims)

ApplyClaims implements the SignOption interface by calling the underlying function.

This method enables SignOptionFunc to satisfy the SignOption interface by simply invoking the wrapped function with the provided Claims pointer. It provides the bridge between functional and interface-based SignOption usage.

**Parameters**:

  • c: Pointer to Claims struct to be modified by the function

**Behavior**: Directly calls the wrapped function with the provided Claims pointer, delegating all claim modification logic to the function implementation.

**Error Handling**: Since the SignOption interface doesn't support error returns, any error handling must be performed within the wrapped function, typically through logging or panic for critical failures.

**Thread Safety**: The thread safety of this method depends entirely on the implementation of the wrapped function. Functions that only modify the provided Claims struct are generally safe.

Example usage:

// The function is called automatically during token signing
customOption := jwt.SignOptionFunc(func(c *jwt.Claims) {
    c.Issuer = "myapp.com"
    c.Subject = "system"
})

// ApplyClaims is called internally by the Sign function
token, err := jwt.Sign(jwt.HS256, key, userClaims, customOption)

// Direct usage (rarely needed)
var claims jwt.Claims
customOption.ApplyClaims(&claims)
fmt.Printf("Issuer: %s", claims.Issuer) // Output: myapp.com

type TokenPair added in v0.0.4

type TokenPair struct {
	AccessToken  json.RawMessage `json:"access_token,omitempty"`
	RefreshToken json.RawMessage `json:"refresh_token,omitempty"`
}

TokenPair represents a standard OAuth2/JWT token response containing both access and refresh tokens.

This structure is designed to be JSON-serialized and sent to clients as part of authentication responses. The tokens are stored as json.RawMessage to preserve their exact byte representation and avoid unnecessary parsing.

The structure follows OAuth2 conventions with "access_token" and "refresh_token" field names, making it compatible with standard OAuth2 clients and libraries.

Example JSON output:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

func NewTokenPair added in v0.0.4

func NewTokenPair(accessToken, refreshToken []byte) TokenPair

NewTokenPair creates a TokenPair from raw access and refresh token bytes.

The function automatically quotes the token bytes to create valid JSON string values. This is useful when you have raw JWT tokens that need to be included in a JSON response.

Either token can be nil/empty if you only want to include one token in the response. The omitempty tags will exclude empty tokens from the JSON output.

Example:

accessToken, _ := jwt.Sign(jwt.HS256, key, accessClaims, jwt.MaxAge(15*time.Minute))
refreshToken, _ := jwt.Sign(jwt.HS256, key, refreshClaims, jwt.MaxAge(7*24*time.Hour))

pair := jwt.NewTokenPair(accessToken, refreshToken)

// Send as JSON response
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(pair)

type TokenValidator

type TokenValidator interface {
	// ValidateToken accepts the token, the claims extracted from that
	// and any error that may caused by claims validation (e.g. ErrExpired)
	// or the previous validator.
	// A token validator can skip the builtin validation and return a nil error.
	// Usage:
	//  func(v *myValidator) ValidateToken(token []byte, standardClaims Claims, err error) error {
	//    if err!=nil { return err } <- to respect the previous error
	//    // otherwise return nil or any custom error.
	//  }
	//
	// Look `Blocklist`, `Expected` and `Leeway` for builtin implementations.
	ValidateToken(token []byte, standardClaims Claims, err error) error
}

TokenValidator provides further token and claims validation.

type TokenValidatorFunc added in v0.0.3

type TokenValidatorFunc func(token []byte, standardClaims Claims, err error) error

TokenValidatorFunc is the interface-as-function shortcut for a TokenValidator.

func Future added in v0.0.14

func Future(dur time.Duration) TokenValidatorFunc

Future creates a TokenValidator that allows tokens issued slightly in the future.

This validator provides tolerance for clock skew between different systems by accepting tokens that appear to be issued in the future, up to the specified duration. Without this tolerance, legitimate tokens might be rejected due to minor time differences between servers.

The validation logic: if (now + duration) < issued_at_time, still reject the token. Otherwise, accept tokens that would normally be rejected for future issuance.

Common use cases:

  • Compensating for clock drift between authentication and resource servers
  • Handling timezone discrepancies in distributed systems
  • Allowing for minor network delays in token propagation
  • Testing scenarios with slightly misaligned system clocks

Example:

// Allow tokens issued up to 60 seconds in the future
futureValidator := jwt.Future(60 * time.Second)

verifiedToken, err := jwt.Verify(alg, key, token, futureValidator)
// Token is accepted even if "iat" is up to 60 seconds in the future

Note: This only affects tokens that would otherwise fail with ErrIssuedInTheFuture. Tokens without "iat" claims or with past issuance times are unaffected.

func Leeway added in v0.0.3

func Leeway(leeway time.Duration) TokenValidatorFunc

Leeway creates a TokenValidator that adds a buffer time before token expiration.

This validator provides "leeway" by rejecting tokens that will expire within the specified duration, even if they are technically still valid. This is useful to prevent race conditions where a token expires between validation and use.

The validation logic: if (now + leeway) > expiration_time, reject the token.

Common use cases:

  • Database operations that might take several seconds to complete
  • API calls that involve multiple service hops
  • Batch processing where token might expire during execution
  • Network latency compensation in distributed systems

Example:

// Reject tokens expiring within 30 seconds
leewayValidator := jwt.Leeway(30 * time.Second)

verifiedToken, err := jwt.Verify(alg, key, token, leewayValidator)
// Token is rejected if it expires within 30 seconds

Note: This only affects tokens that have an "exp" claim. Tokens without expiration are not affected by leeway validation.

func (TokenValidatorFunc) ValidateToken added in v0.0.3

func (fn TokenValidatorFunc) ValidateToken(token []byte, standardClaims Claims, err error) error

ValidateToken completes the ValidateToken interface. It calls itself.

type UnverifiedToken added in v0.0.8

type UnverifiedToken struct {
	Header    []byte // Decoded JWT header JSON bytes
	Payload   []byte // Decoded JWT payload JSON bytes
	Signature []byte // Decoded signature bytes (unverified)
	// contains filtered or unexported fields
}

UnverifiedToken represents the parsed components of a JWT token without verification.

This structure contains the three decoded parts of a JWT token (header, payload, signature) as raw byte slices. It's returned by the Decode function and provides access to token components without performing any cryptographic verification.

**SECURITY WARNING**: This structure contains unverified token data. The signature has not been validated, expiration has not been checked, and the token's authenticity is not guaranteed. Only use this with tokens from fully trusted sources.

Fields:

  • Header: JSON header containing algorithm and token type information
  • Payload: JSON payload containing claims and application data
  • Signature: Raw signature bytes used for verification (not validated)

Common usage patterns:

  • Development and debugging to inspect token structure
  • Internal applications where tokens are generated and consumed by the same service
  • Logging and monitoring to extract metadata without verification overhead
  • Testing and validation tools that need to examine token content

Use the Claims method to unmarshal the Payload into structured Go types. For production use with external tokens, use verified token structures instead.

Example:

unverified, err := jwt.Decode(tokenBytes)
if err != nil {
    return err
}

// Inspect header
var header struct {
    Alg string `json:"alg"`
    Typ string `json:"typ"`
}
json.Unmarshal(unverified.Header, &header)
fmt.Printf("Algorithm: %s\n", header.Alg)

// Extract claims
var claims jwt.Map
unverified.Claims(&claims)
fmt.Printf("Subject: %v\n", claims["sub"])

func Decode added in v0.0.8

func Decode(token []byte) (*UnverifiedToken, error)

Decode parses a JWT token into its components WITHOUT any verification or validation.

This function performs raw parsing of JWT tokens to extract header, payload, and signature components without cryptographic verification. It's designed for scenarios where token content needs to be inspected without validating authenticity.

**SECURITY WARNING**: This function does NOT verify:

  • Token signature (authenticity)
  • Token expiration (exp claim)
  • Token validity periods (nbf, iat claims)
  • Algorithm security
  • Any other security-related validations

Use cases for Decode (all require trusted sources):

  • Extracting claims from tokens generated by the same application
  • Reading token metadata for logging or debugging
  • Inspecting token structure during development
  • Processing tokens from fully trusted internal sources

For security-critical applications, use Verify, VerifyEncrypted, or VerifyWithHeaderValidator functions instead.

Parameters:

  • token: JWT token bytes in compact form (header.payload.signature)

Returns:

  • *UnverifiedToken: Parsed token components (header, payload, signature)
  • error: Parsing errors including:
  • ErrTokenForm if token structure is invalid
  • Base64 decoding errors for malformed components

The returned UnverifiedToken contains raw decoded bytes for each component. Use its Claims method to unmarshal the payload into structured data.

Example:

// Only use with trusted tokens!
token := []byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c")

unverified, err := jwt.Decode(token)
if err != nil {
    log.Fatalf("Failed to decode token: %v", err)
}

// Extract claims without verification
var claims jwt.Map
err = unverified.Claims(&claims)
if err != nil {
    log.Fatalf("Failed to parse claims: %v", err)
}

fmt.Printf("Subject: %v\n", claims["sub"])
// WARNING: This token was NOT verified!

func (*UnverifiedToken) Alg added in v0.1.16

func (t *UnverifiedToken) Alg() (Alg, error)

Alg returns the algorithm used in the original token header. It extracts the "alg" field from the JWT header and returns the corresponding Alg implementation.

If the header is malformed or the algorithm is unknown, it returns an error. This method is useful for determining the algorithm used to sign the token.

func (*UnverifiedToken) Claims added in v0.0.8

func (t *UnverifiedToken) Claims(dest any) error

Claims unmarshals the JWT payload into the provided destination structure.

This method decodes the raw JSON payload bytes into a Go data structure, enabling access to claims data in a type-safe manner. The destination can be any type that's compatible with JSON unmarshaling.

**SECURITY WARNING**: The payload data has not been cryptographically verified. Do not trust this data for security-critical decisions unless the token source is completely trusted and internal to your application.

Parameters:

  • dest: Pointer to the destination where claims will be unmarshaled

Returns:

  • error: JSON unmarshaling error if the payload is malformed or incompatible

Supported destination types:

  • jwt.Map for dynamic claims access
  • Custom structs with json tags for typed claims
  • jwt.RegisteredClaims for standard JWT claims
  • Any type compatible with json.Unmarshal

Example usage:

unverified, _ := jwt.Decode(tokenBytes)

// Extract to dynamic map
var claims jwt.Map
if err := unverified.Claims(&claims); err != nil {
    return err
}
fmt.Printf("Subject: %v\n", claims["sub"])

// Extract to custom struct
type MyClaims struct {
    UserID   string    `json:"sub"`
    Username string    `json:"username"`
    IssuedAt time.Time `json:"iat"`
}
var myClaims MyClaims
if err := unverified.Claims(&myClaims); err != nil {
    return err
}

// Extract standard claims
var registered jwt.RegisteredClaims
if err := unverified.Claims(&registered); err != nil {
    return err
}
fmt.Printf("Expires: %v\n", registered.ExpiresAt)

func (*UnverifiedToken) Enrich added in v0.1.16

func (t *UnverifiedToken) Enrich(key PrivateKey, extraClaims any) ([]byte, error)

Enrich creates a new JWT token by merging the original token's claims with additional claims.

This method allows you to extend the original token's payload with new claims while preserving the original token's header and signature structure.

It uses the same algorithm and key as the original token to ensure the new token is valid.

Parameters:
 - key: PrivateKey used to sign the new token
 - extraClaims: Map of additional claims to merge with the original token's payload

Returns:

  • []byte: New JWT token with merged claims
  • error: Error if the original token's algorithm cannot be determined or if merging fails

Example usage:

originalToken, _ := jwt.Decode(tokenBytes)
extraClaims := map[string]any{
    "role": "admin",
    "permissions": []string{"read", "write"},
}
newToken, err := originalToken.Enrich(signingKey, extraClaims)

//	if err != nil {
	    log.Fatalf("Failed to enrich token: %v", err)
	}

This newToken will have the original header and signature, but with the additional claims merged in. Note: This method does not verify the original token; it assumes the original token is valid.

func (*UnverifiedToken) Kid added in v0.1.16

func (t *UnverifiedToken) Kid() (string, error)

Kid returns the Key ID (kid) from the original token header. It extracts the "kid" field from the JWT header and returns its value. If the header is malformed or the "kid" field is missing, it returns an error.

type VerifiedToken

type VerifiedToken struct {
	// Token contains the original JWT token bytes as provided to verification functions.
	// This is useful for logging, passing to other services, or debugging purposes.
	Token []byte

	// Header contains the decoded JWT header as raw JSON bytes.
	// The header includes algorithm information and any custom header fields.
	Header []byte

	// Payload contains the decoded JWT payload as raw JSON bytes.
	// For encrypted tokens, this contains the decrypted payload.
	// Use the Claims() method to parse this into structured data.
	Payload []byte

	// Signature contains the decoded JWT signature bytes.
	// This is the raw signature that was cryptographically verified.
	Signature []byte

	// StandardClaims contains parsed standard JWT claims extracted from the payload.
	// These claims have been validated during the verification process.
	// Includes: exp, nbf, iat, iss, sub, aud, jti, and origin_jti.
	StandardClaims Claims
}

VerifiedToken contains the results of successful JWT token verification.

This structure holds all the decoded components of a verified JWT token, providing access to both the raw token data and parsed standard claims. It serves as the return value for all successful token verification operations.

**Field Details**:

  • Token: Original token bytes as provided to verification functions
  • Header: Decoded JWT header (base64url decoded JSON)
  • Payload: Decoded JWT payload (base64url decoded, possibly decrypted JSON)
  • Signature: Decoded JWT signature bytes (base64url decoded)
  • StandardClaims: Parsed standard JWT claims (exp, nbf, iat, iss, sub, aud, jti)

**Security Assurance**: The presence of a VerifiedToken instance guarantees:

  • Signature has been cryptographically verified
  • Token format is valid (header.payload.signature)
  • Standard claims have passed timing validation
  • Any provided custom validators have approved the token
  • Optional header validation has passed (if HeaderValidator was used)
  • Optional payload decryption was successful (if decrypt function was used)

**Usage Patterns**:

  • Access standard claims directly via StandardClaims field
  • Extract custom claims using the Claims() method
  • Inspect raw token components for debugging or logging
  • Pass to other functions requiring verified token context

**Thread Safety**: VerifiedToken instances are safe for concurrent read access once created, as all fields are populated during verification and not modified.

Example usage:

verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, tokenBytes)
if err != nil {
    log.Printf("Verification failed: %v", err)
    return
}

// Access standard claims directly
subject := verifiedToken.StandardClaims.Subject
issuer := verifiedToken.StandardClaims.Issuer
expiresAt := verifiedToken.StandardClaims.ExpiresAt()

// Check if token is near expiration
timeLeft := verifiedToken.StandardClaims.Timeleft()
if timeLeft < 5*time.Minute {
    log.Printf("Token expires soon: %v remaining", timeLeft)
}

// Extract custom claims
var userClaims struct {
    UserID      string   `json:"user_id"`
    Role        string   `json:"role"`
    Permissions []string `json:"permissions"`
}
err = verifiedToken.Claims(&userClaims)
if err != nil {
    log.Printf("Failed to parse custom claims: %v", err)
}

// Access raw components for logging
log.Printf("Verified token: %s", string(verifiedToken.Token))
log.Printf("Header: %s", string(verifiedToken.Header))
log.Printf("Payload: %s", string(verifiedToken.Payload))

**Memory Considerations**: VerifiedToken holds references to the decoded token components. For high-throughput applications, consider extracting needed claims and discarding the VerifiedToken to free memory.

func Verify

func Verify(alg Alg, key PublicKey, token []byte, validators ...TokenValidator) (*VerifiedToken, error)

Verify validates and decodes a JWT token, returning verified token information.

This is the primary function for JWT token verification. It performs signature validation, standard claims verification, and optional custom validation through TokenValidator implementations. The function ensures token integrity and validity.

**Parameters**:

  • alg: Algorithm used to sign the token (must match signing algorithm)
  • key: Public key material for verification (corresponding to signing key)
  • token: JWT token bytes to verify and decode
  • validators: Optional TokenValidator implementations for custom validation

**Verification Process**:

  1. Token format validation (header.payload.signature structure)
  2. Signature verification using algorithm and public key
  3. Payload decoding and JSON parsing
  4. Standard claims validation (exp, nbf, iat if present)
  5. Custom validator execution (if provided)

**Standard Claims Validation**: Automatically validates timing claims:

  • "exp" (expiry): Ensures token hasn't expired
  • "nbf" (not before): Ensures token is active
  • "iat" (issued at): Ensures token wasn't issued in the future

**Return Value**: VerifiedToken containing:

  • Original token bytes
  • Decoded header, payload, and signature
  • Parsed standard claims
  • Claims() method for custom claim extraction

**Error Conditions**:

  • Invalid token format or structure
  • Signature verification failure
  • Algorithm mismatch
  • Standard claims validation failure (expired, not yet valid, etc.)
  • Custom validator rejection

**Security Features**:

  • Cryptographic signature verification
  • Timing-based access control
  • Algorithm validation to prevent algorithm confusion attacks
  • Extensible validation through TokenValidator interface

Example usage:

// Basic token verification
verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, tokenBytes)
if err != nil {
    log.Printf("Token verification failed: %v", err)
    return
}

// Access standard claims
fmt.Printf("Subject: %s", verifiedToken.StandardClaims.Subject)
fmt.Printf("Expires: %v", verifiedToken.StandardClaims.ExpiresAt())

// Extract custom claims
var customClaims struct {
    UserID   string   `json:"user_id"`
    Role     string   `json:"role"`
    Permissions []string `json:"permissions"`
}
err = verifiedToken.Claims(&customClaims)
if err != nil {
    log.Printf("Failed to parse custom claims: %v", err)
    return
}

// Using map for flexible claim access
var allClaims map[string]any
err = verifiedToken.Claims(&allClaims)
if err == nil {
    userID := allClaims["user_id"].(string)
    role := allClaims["role"].(string)
}

// With custom validators
verifiedToken, err := jwt.Verify(jwt.RS256, rsaPublicKey, tokenBytes,
    jwt.Expected{Issuer: "myapp.com"},
    jwt.Leeway(5 * time.Minute),
    customValidator)

**Algorithm Support**: Works with all supported algorithms (HMAC, RSA, ECDSA, EdDSA). The key parameter type varies by algorithm family - use appropriate key type.

See VerifyEncrypted for encrypted payload tokens and TokenValidator implementations for custom validation logic.

func VerifyEncrypted added in v0.0.3

func VerifyEncrypted(alg Alg, key PublicKey, decrypt InjectFunc, token []byte, validators ...TokenValidator) (*VerifiedToken, error)

VerifyEncrypted validates and decodes a JWT token with encrypted payload.

This function extends Verify by adding payload decryption capability. It verifies the token signature and then decrypts the payload before parsing claims. This is used for tokens created with SignEncrypted that contain confidential data.

**Parameters**:

  • alg: Algorithm used to sign the token (must match signing algorithm)
  • key: Public key material for signature verification
  • decrypt: Function to decrypt the payload (see InjectFunc)
  • token: JWT token bytes with encrypted payload to verify and decode
  • validators: Optional TokenValidator implementations for custom validation

**Verification and Decryption Process**:

  1. Token format validation and signature verification
  2. Payload base64url decoding
  3. Payload decryption using the decrypt function
  4. Decrypted payload JSON parsing
  5. Standard claims validation and custom validator execution

**InjectFunc Decryption**: The decrypt function receives []byte (base64-decoded encrypted payload) and returns []byte (decrypted JSON) and error. It's called after base64url decoding but before JSON unmarshaling.

**Security Requirements**:

  • Correct decryption key corresponding to encryption key used during signing
  • Proper verification key corresponding to signing key
  • Authenticated encryption recommended (see GCM function)
  • Secure key management for both signing and encryption keys

**Error Conditions**:

  • All standard Verify errors (signature, format, timing)
  • Decryption failures (wrong key, corrupted data, authentication failure)
  • JSON parsing errors after decryption

Example usage:

// Using AES-GCM encryption (see GCM function)
encryptKey := []byte("my-32-byte-encryption-key-here!")
encrypt, decrypt := jwt.GCM(encryptKey, nil)

// Verify encrypted token
verifiedToken, err := jwt.VerifyEncrypted(jwt.HS256, signingKey, decrypt, tokenBytes)
if err != nil {
    log.Printf("Token verification/decryption failed: %v", err)
    return
}

// Extract sensitive claims (now decrypted)
var sensitiveData struct {
    SSN         string `json:"ssn"`
    CreditCard  string `json:"credit_card"`
    BankAccount string `json:"bank_account"`
    UserID      string `json:"user_id"`
}
err = verifiedToken.Claims(&sensitiveData)

// Custom decryption function
customDecrypt := func(encrypted []byte) ([]byte, error) {
    // Your custom decryption logic here
    return decryptedJSON, nil
}

verifiedToken, err := jwt.VerifyEncrypted(jwt.RS256, rsaPublicKey,
    customDecrypt, tokenBytes, jwt.Expected{Issuer: "secure-service"})

// With multiple validators for encrypted tokens
verifiedToken, err := jwt.VerifyEncrypted(jwt.ES256, ecdsaPublicKey, decrypt,
    encryptedToken,
    jwt.Leeway(time.Minute),
    jwt.Blocklist(revokedTokens),
    customSecurityValidator)

**Important Notes**:

  • Tokens created with SignEncrypted MUST be verified with VerifyEncrypted
  • The decrypt function must use the same algorithm and key as encryption
  • Decryption errors are treated as verification failures
  • Standard claims validation occurs after successful decryption

**Performance Considerations**:

  • Decryption adds computational overhead compared to standard verification
  • Use appropriate encryption algorithms for your performance requirements
  • Consider caching decrypted results for frequently accessed tokens

See GCM for authenticated encryption, SignEncrypted for creating encrypted tokens, and Verify for standard (non-encrypted) token verification.

func VerifyEncryptedWithHeaderValidator added in v0.0.14

func VerifyEncryptedWithHeaderValidator(alg Alg, key PublicKey, decrypt InjectFunc, token []byte, headerValidator HeaderValidator, validators ...TokenValidator) (*VerifiedToken, error)

VerifyEncryptedWithHeaderValidator validates a JWT token with encrypted payload and custom header validation.

This function combines the functionality of VerifyEncrypted and VerifyWithHeaderValidator, providing both payload decryption and custom header validation in a single operation. It's ideal for scenarios requiring both payload confidentiality and header metadata validation.

**Parameters**:

  • alg: Algorithm used to sign the token (must match signing algorithm)
  • key: Public key material for signature verification
  • decrypt: Function to decrypt the payload (see InjectFunc)
  • token: JWT token bytes with encrypted payload to verify and decode
  • headerValidator: Custom validator for JWT header fields
  • validators: Optional TokenValidator implementations for payload validation

**Processing Order**:

  1. Token format validation and header decoding
  2. Custom header validation using headerValidator
  3. Signature verification using algorithm and key
  4. Payload base64url decoding and decryption
  5. Standard claims validation and custom validator execution

**Combined Benefits**:

  • Header metadata validation for token identification and routing
  • Payload confidentiality through decryption
  • Standard claims validation after decryption
  • Complete JWT security verification workflow

**Security Considerations**:

  • Headers remain unencrypted and are validated before payload processing
  • Payload is encrypted and requires correct decryption function
  • Both header and payload validation must pass for successful verification
  • Multiple layers of validation provide defense in depth

**Use Cases**:

  • Multi-tenant systems with encrypted user data and tenant header validation
  • Key rotation systems requiring both key ID validation and payload decryption
  • Content-type validation with encrypted sensitive data
  • Certificate-based systems with encrypted payloads

Example usage:

// Multi-tenant encrypted token with header validation
encryptKey := []byte("tenant-specific-encryption-key!")
encrypt, decrypt := jwt.GCM(encryptKey, nil)

tenantValidator := func(header map[string]any) error {
    kid, ok := header["kid"].(string)
    if !ok {
        return errors.New("missing key ID in header")
    }

    tenantID, ok := header["tenant_id"].(string)
    if !ok || !isValidTenant(tenantID) {
        return errors.New("invalid tenant ID")
    }

    if !isValidKeyForTenant(kid, tenantID) {
        return errors.New("key ID not valid for tenant")
    }

    return nil
}

verifiedToken, err := jwt.VerifyEncryptedWithHeaderValidator(
    jwt.RS256, rsaKey, decrypt, tokenBytes, tenantValidator,
    jwt.Expected{Issuer: "tenant-service"})

// Certificate-based validation with encrypted payload
certValidator := func(header map[string]any) error {
    x5t, ok := header["x5t"].(string)
    if ok && !isValidCertThumbprint(x5t) {
        return errors.New("invalid certificate thumbprint")
    }

    jku, ok := header["jku"].(string)
    if ok && !strings.HasPrefix(jku, "https://trusted.certs.com/") {
        return errors.New("untrusted certificate URL")
    }

    return nil
}

verifiedToken, err := jwt.VerifyEncryptedWithHeaderValidator(
    jwt.ES256, ecdsaKey, decrypt, encryptedToken, certValidator,
    jwt.Leeway(time.Minute),
    jwt.Blocklist(revokedTokens))

// Extract decrypted sensitive data after all validation
var sensitiveData struct {
    PersonalInfo map[string]any `json:"personal_info"`
    Financial    map[string]any `json:"financial"`
}
err = verifiedToken.Claims(&sensitiveData)

**Error Conditions**:

  • All standard Verify and VerifyEncrypted errors
  • Header validation failures from headerValidator
  • Decryption failures (wrong key, corrupted data)
  • Missing or invalid header fields

**Performance Considerations**:

  • Header validation occurs before expensive decryption operations
  • Early header rejection can save computational resources
  • Combine validation checks efficiently in headerValidator function

See HeaderValidator for header validation patterns, GCM for authenticated encryption, and VerifyEncrypted for encrypted-only token verification.

func VerifyWithHeaderValidator added in v0.0.14

func VerifyWithHeaderValidator(alg Alg, key PublicKey, token []byte, headerValidator HeaderValidator, validators ...TokenValidator) (*VerifiedToken, error)

VerifyWithHeaderValidator validates a JWT token with custom header validation.

This function extends Verify by adding custom header validation capability. It allows validation of JWT header fields beyond the standard "alg" and "typ" fields, enabling verification of custom header claims like "kid", "jku", etc.

**Parameters**:

  • alg: Algorithm used to sign the token (must match signing algorithm)
  • key: Public key material for signature verification
  • token: JWT token bytes to verify and decode
  • headerValidator: Custom validator for JWT header fields
  • validators: Optional TokenValidator implementations for payload validation

**Header Validation Process**:

  1. Token format validation and header decoding
  2. Custom header validation using headerValidator
  3. Standard signature verification
  4. Payload processing and claims validation
  5. Custom payload validator execution

**HeaderValidator Interface**: The headerValidator receives the decoded header as a map[string]any and returns an error if validation fails. This allows custom logic for validating header fields.

**Common Header Validation Use Cases**:

  • "kid" (Key ID): Validate key identifier matches expected values
  • "jku" (JWK Set URL): Verify JWK Set URL is from trusted domain
  • "x5t" (X.509 Thumbprint): Validate certificate thumbprint
  • "cty" (Content Type): Ensure correct content type
  • Custom fields: Application-specific header validation

**Security Benefits**:

  • Prevents use of tokens with invalid or malicious headers
  • Enables key rotation validation through "kid" checks
  • Supports certificate-based validation
  • Allows whitelist/blacklist validation of header values

Example usage:

// Validate key identifier
keyIDValidator := func(header map[string]any) error {
    kid, ok := header["kid"].(string)
    if !ok {
        return errors.New("missing kid header")
    }
    if !isValidKeyID(kid) {
        return errors.New("invalid key identifier")
    }
    return nil
}

verifiedToken, err := jwt.VerifyWithHeaderValidator(jwt.RS256, rsaKey,
    tokenBytes, keyIDValidator)

// Validate JWK Set URL
jkuValidator := func(header map[string]any) error {
    jku, ok := header["jku"].(string)
    if ok && !strings.HasPrefix(jku, "https://trusted.domain.com/") {
        return errors.New("untrusted JWK Set URL")
    }
    return nil
}

// Combined header and payload validation
verifiedToken, err := jwt.VerifyWithHeaderValidator(jwt.ES256, ecdsaKey,
    tokenBytes, jkuValidator,
    jwt.Expected{Issuer: "trusted-issuer"},
    jwt.Leeway(time.Minute))

// Multiple header field validation
multiHeaderValidator := func(header map[string]any) error {
    // Validate key ID
    if kid, ok := header["kid"].(string); ok {
        if !isValidKeyID(kid) {
            return errors.New("invalid key ID")
        }
    }

    // Validate content type
    if cty, ok := header["cty"].(string); ok {
        if cty != "application/json" {
            return errors.New("unsupported content type")
        }
    }

    return nil
}

verifiedToken, err := jwt.VerifyWithHeaderValidator(jwt.HS256, hmacKey,
    tokenBytes, multiHeaderValidator)

**Error Conditions**:

  • All standard Verify errors
  • Header validation failures from headerValidator
  • Missing or invalid header fields

**Performance Note**: Header validation adds minimal overhead as it operates on the already-decoded header data.

See HeaderValidator type definition and VerifyEncryptedWithHeaderValidator for encrypted payload tokens with header validation.

func (*VerifiedToken) Claims

func (t *VerifiedToken) Claims(dest any) error

Claims extracts and unmarshals the token's payload into the provided destination.

This method is the primary way to access custom claims from a verified JWT token. It unmarshals the token's payload (claims) into the destination pointer, allowing access to application-specific data beyond the standard JWT claims.

**Parameters**:

  • dest: Pointer to destination struct, map, or any JSON-unmarshallable type

**Supported Destination Types**:

  • Struct with JSON tags: For type-safe custom claim extraction
  • map[string]any: For flexible dynamic claim access
  • Custom types implementing json.Unmarshaler
  • Any pointer to JSON-compatible Go types

**JSON Unmarshaling**: Uses the package-level Unmarshal function, which respects the configured JSON unmarshaling behavior and any custom unmarshalers.

**Standard Claims**: The StandardClaims field is always populated and validated during verification. No additional validation is needed for standard timing claims (exp, nbf, iat) as they are automatically verified.

**Performance Note**: This method performs JSON unmarshaling on each call. For frequently accessed claims, consider unmarshaling once and caching the result.

**Error Conditions**:

  • JSON unmarshaling errors (invalid JSON, type mismatches)
  • Destination is not a pointer
  • Incompatible types between JSON and destination

Example usage:

verifiedToken, err := jwt.Verify(jwt.HS256, secretKey, tokenBytes)
if err != nil {
    return err
}

// Extract to custom struct
var userClaims struct {
    UserID      string   `json:"user_id"`
    Role        string   `json:"role"`
    Permissions []string `json:"permissions"`
    Email       string   `json:"email"`
}
err = verifiedToken.Claims(&userClaims)
if err != nil {
    return fmt.Errorf("failed to parse user claims: %w", err)
}

// Extract to map for dynamic access
var allClaims map[string]any
err = verifiedToken.Claims(&allClaims)
if err != nil {
    return err
}

// Access claims dynamically
if userID, ok := allClaims["user_id"].(string); ok {
    log.Printf("User ID: %s", userID)
}

// Combine with standard claims
log.Printf("User %s (%s) expires at %v",
    userClaims.UserID,
    userClaims.Role,
    verifiedToken.StandardClaims.ExpiresAt())

// Extract partial claims
var roleInfo struct {
    Role        string   `json:"role"`
    Permissions []string `json:"permissions"`
}
err = verifiedToken.Claims(&roleInfo)

// Type assertion for specific claim types
var metadata struct {
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
    Version   int       `json:"version"`
}
err = verifiedToken.Claims(&metadata)

**Best Practices**:

  • Use struct types for known claim schemas (type safety)
  • Use maps for dynamic or unknown claim structures
  • Handle unmarshaling errors appropriately
  • Consider caching unmarshaled results for repeated access
  • Validate custom claims as needed (standard claims are pre-validated)

Directories

Path Synopsis
_examples
basic command
blocklist command
custom-header command
middleware command
multiple-kids command
required command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL