Documentation
¶
Overview ¶
Package jwt provides JSON Web Token authentication middleware for celeris.
The middleware extracts a JWT from a configurable source (header, query parameter, cookie, form field, or URL parameter), validates it using HMAC, RSA, ECDSA, EdDSA, or RSA-PSS keys, and stores the parsed token and claims in the context store under TokenKey and ClaimsKey. Failed authentication returns 401.
All public types (Token, Claims, MapClaims, RegisteredClaims, SigningMethod, etc.) are re-exported from the internal parser.
Simple usage with a symmetric HMAC secret:
server.Use(jwt.New(jwt.Config{
SigningKey: []byte("my-secret-key"),
}))
JWKS auto-discovery (e.g. Auth0, Keycloak):
server.Use(jwt.New(jwt.Config{
JWKSURL: "https://example.com/.well-known/jwks.json",
JWKSRefresh: 30 * time.Minute,
}))
Retrieving Token and Claims ¶
token := jwt.TokenFromContext(c) claims, ok := jwt.ClaimsFromContext[jwt.MapClaims](c)
Token Lookup ¶
Config.TokenLookup supports comma-separated sources tried in order. Format: "source:name[:prefix]". Sources: header, query, cookie, form, param.
Custom Claims ¶
Use Config.ClaimsFactory for custom struct claims types:
jwt.New(jwt.Config{
SigningKey: secret,
ClaimsFactory: func() jwt.Claims { return &MyClaims{} },
})
Creating Tokens ¶
token, err := jwt.SignToken(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": "user-123",
"exp": float64(time.Now().Add(time.Hour).Unix()),
}, []byte("secret"))
Security Best Practices ¶
Always set "exp" on issued tokens. Use "aud" with WithAudience to prevent token confusion. Prefer asymmetric algorithms (RS256, ES256) in multi-service deployments. Store keys in environment variables or a secrets manager. Serve JWKS endpoints over HTTPS.
Algorithm-Key-Type Binding ¶
- HS256/HS384/HS512: []byte
- RS256/RS384/RS512/PS256/PS384/PS512: *rsa.PublicKey / *rsa.PrivateKey
- ES256/ES384/ES512: *ecdsa.PublicKey / *ecdsa.PrivateKey
- EdDSA: ed25519.PublicKey / ed25519.PrivateKey
Skipping ¶
Set Config.Skip to bypass dynamically, or Config.SkipPaths for exact-match path exclusions.
Index ¶
- Constants
- Variables
- func ClaimsFromContext[T jwtparse.Claims](c *celeris.Context) (T, bool)
- func ClaimsFromContextWithKey[T jwtparse.Claims](c *celeris.Context, key string) (T, bool)
- func New(config ...Config) celeris.HandlerFunc
- func SignToken(method SigningMethod, claims Claims, key any) (string, error)
- func TokenFromContext(c *celeris.Context) *jwtparse.Token
- func TokenFromContextWithKey(c *celeris.Context, key string) *jwtparse.Token
- type Audience
- type Claims
- type Config
- type Header
- type Keyfunc
- type MapClaims
- type NumericDate
- type ParserOption
- type RegisteredClaims
- type SigningMethod
- type Token
Examples ¶
Constants ¶
const ClaimsKey = "jwt_claims"
ClaimsKey is the context store key for the token's claims.
const TokenKey = "jwt_token"
TokenKey is the context store key for the parsed *jwt.Token.
Variables ¶
var ( SigningMethodHS256 = jwtparse.SigningMethodHS256 SigningMethodHS384 = jwtparse.SigningMethodHS384 SigningMethodHS512 = jwtparse.SigningMethodHS512 SigningMethodRS256 = jwtparse.SigningMethodRS256 SigningMethodRS384 = jwtparse.SigningMethodRS384 SigningMethodRS512 = jwtparse.SigningMethodRS512 SigningMethodES256 = jwtparse.SigningMethodES256 SigningMethodES384 = jwtparse.SigningMethodES384 SigningMethodES512 = jwtparse.SigningMethodES512 SigningMethodEdDSA = jwtparse.SigningMethodEdDSA SigningMethodPS256 = jwtparse.SigningMethodPS256 SigningMethodPS384 = jwtparse.SigningMethodPS384 SigningMethodPS512 = jwtparse.SigningMethodPS512 )
Signing method singletons.
var ( ErrTokenMalformed = jwtparse.ErrTokenMalformed ErrTokenExpired = jwtparse.ErrTokenExpired ErrTokenNotValidYet = jwtparse.ErrTokenNotValidYet ErrTokenSignatureInvalid = jwtparse.ErrTokenSignatureInvalid ErrAlgNone = jwtparse.ErrAlgNone ErrTokenUnverifiable = jwtparse.ErrTokenUnverifiable ErrTokenUsedBeforeIssued = jwtparse.ErrTokenUsedBeforeIssued ErrInvalidIssuer = jwtparse.ErrInvalidIssuer ErrInvalidAudience = jwtparse.ErrInvalidAudience ErrInvalidSubject = jwtparse.ErrInvalidSubject )
Sentinel errors from the parser.
var ( WithValidMethods = jwtparse.WithValidMethods WithLeeway = jwtparse.WithLeeway WithIssuer = jwtparse.WithIssuer WithAudience = jwtparse.WithAudience WithSubject = jwtparse.WithSubject )
Parser option constructors.
var ErrJWTExpired = &celeris.HTTPError{Code: 401, Message: "Unauthorized", Err: errors.New("jwt: token has expired")}
ErrJWTExpired is returned when the token's exp claim is in the past.
var ErrJWTMalformed = &celeris.HTTPError{Code: 401, Message: "Unauthorized", Err: errors.New("jwt: token is malformed")}
ErrJWTMalformed is returned when the token cannot be parsed (bad encoding, wrong number of segments, etc.).
var ErrTokenInvalid = &celeris.HTTPError{Code: 401, Message: "Unauthorized", Err: errors.New("jwt: invalid or expired token")}
ErrTokenInvalid is returned when the token fails validation for reasons other than expiration or malformation (e.g., bad signature, unknown kid).
var ErrTokenMissing = &celeris.HTTPError{Code: 401, Message: "Unauthorized", Err: errors.New("jwt: missing or malformed token")}
ErrTokenMissing is returned when no token is found in the request.
ErrUnauthorized is returned when authentication fails.
Functions ¶
func ClaimsFromContext ¶
ClaimsFromContext returns the token claims from the context store, typed to the requested claims type T, using the default ClaimsKey. Returns the zero value and false if no claims were stored or the type assertion fails. If the middleware was configured with a custom ClaimsContextKey, use ClaimsFromContextWithKey instead.
Example ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Retrieve typed claims in a downstream handler.
_ = func(c *celeris.Context) error {
claims, ok := jwt.ClaimsFromContext[jwt.MapClaims](c)
if !ok {
return c.String(401, "no claims")
}
sub, _ := claims["sub"].(string)
return c.String(200, "Hello %s", sub)
}
}
Output:
func ClaimsFromContextWithKey ¶
ClaimsFromContextWithKey returns the token claims from the context store, typed to the requested claims type T, using the specified key. Returns the zero value and false if no claims were stored or the type assertion fails.
func New ¶
func New(config ...Config) celeris.HandlerFunc
New creates a JWT authentication middleware with the given config.
Example ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Simple HMAC authentication with a shared secret.
_ = jwt.New(jwt.Config{
SigningKey: []byte("my-secret-key"),
})
}
Output:
Example (ClaimsFactory) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
type MyClaims struct {
jwt.RegisteredClaims
Role string `json:"role"`
}
func main() {
// Use ClaimsFactory for custom struct claims to avoid data races.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
ClaimsFactory: func() jwt.Claims {
return &MyClaims{}
},
})
}
Output:
Example (Cookie) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Extract token from a cookie.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
TokenLookup: "cookie:jwt",
})
}
Output:
Example (CustomContextKeys) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Store token and claims under custom context keys.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
TokenContextKey: "my_token",
ClaimsContextKey: "my_claims",
})
}
Output:
Example (CustomKeyFunc) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Full control over key resolution.
_ = jwt.New(jwt.Config{
SigningKey: []byte("unused"), // satisfies validation
KeyFunc: func(t *jwt.Token) (any, error) {
// Custom key lookup logic.
return []byte("dynamic-secret"), nil
},
})
}
Output:
Example (ErrorHandler) ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Custom error handler for JSON responses.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
ErrorHandler: func(c *celeris.Context, err error) error {
return c.JSON(401, map[string]string{"error": err.Error()})
},
})
}
Output:
Example (Jwks) ¶
package main
import (
"time"
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// JWKS auto-discovery (e.g. Auth0, Keycloak).
_ = jwt.New(jwt.Config{
JWKSURL: "https://example.com/.well-known/jwks.json",
JWKSRefresh: 30 * time.Minute,
})
}
Output:
Example (MultipleSources) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Try header first, then query, then cookie.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
TokenLookup: "header:Authorization:Bearer ,query:token,cookie:jwt",
})
}
Output:
Example (QueryParam) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Extract token from query parameter instead of header.
_ = jwt.New(jwt.Config{
SigningKey: []byte("secret"),
TokenLookup: "query:token",
})
}
Output:
Example (SigningKeys) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Key rotation with kid-based key selection.
_ = jwt.New(jwt.Config{
SigningKeys: map[string]any{
"key-1": []byte("secret-1"),
"key-2": []byte("secret-2"),
},
})
}
Output:
Example (TokenProcessorFunc) ¶
package main
import (
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// JWE decryption: TokenProcessorFunc decrypts the outer JWE envelope
// before the inner JWS is parsed and validated by the middleware.
_ = jwt.New(jwt.Config{
SigningKey: []byte("signing-secret"),
TokenProcessorFunc: func(token string) (string, error) {
// In a real application, this would decrypt a JWE compact
// serialization (5-part dot-separated) using a private key
// or shared symmetric key, returning the inner JWS token.
//
// Example with a JWE library (pseudocode):
// privKey := loadDecryptionKey()
// jws, err := jwe.Decrypt([]byte(token), privKey)
// return string(jws), err
//
// For this example, we simulate by stripping a "jwe:" prefix.
if len(token) > 4 && token[:4] == "jwe:" {
return token[4:], nil
}
return token, nil
},
})
}
Output:
func SignToken ¶
func SignToken(method SigningMethod, claims Claims, key any) (string, error)
SignToken creates and signs a new JWT.
func TokenFromContext ¶
TokenFromContext returns the parsed JWT token from the context store using the default TokenKey. Returns nil if no token was stored. If the middleware was configured with a custom TokenContextKey, use TokenFromContextWithKey instead.
Example ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/jwt"
)
func main() {
// Retrieve the raw parsed token in a downstream handler.
_ = func(c *celeris.Context) error {
token := jwt.TokenFromContext(c)
if token == nil {
return c.String(401, "no token")
}
return c.String(200, "Algorithm: %s", token.Method.Alg())
}
}
Output:
Types ¶
type Config ¶
type Config struct {
// Skip defines a function to skip this middleware for certain requests.
Skip func(c *celeris.Context) bool
// SkipPaths lists paths to skip (exact match).
SkipPaths []string
// SigningKey is the key used to verify tokens. For HMAC this is a
// []byte secret; for RSA/ECDSA this is the public key.
SigningKey any
// SigningKeys maps kid (Key ID) to verification key for key rotation.
// When set, the token's "kid" header selects the key.
SigningKeys map[string]any
// SigningMethod is the expected signing algorithm.
// Default: jwt.SigningMethodHS256.
SigningMethod jwtparse.SigningMethod
// ValidMethods restricts the allowed signing algorithms.
// Default: []string{SigningMethod.Alg()}.
ValidMethods []string
// TokenLookup defines where to extract the token. Comma-separated
// sources are tried in order. Format: "source:name[:prefix]".
// Supported sources: header, query, cookie, form, param.
// Default: "header:Authorization:Bearer ".
TokenLookup string
// Claims is the template for the claims type. The middleware clones
// this for each request via [cloneClaims]. For built-in types
// ([MapClaims], *[RegisteredClaims]) this is zero-cost. For custom
// struct types, reflect.New is used to create a fresh zero-value
// instance -- the template's field values are NOT copied. For
// maximum control and to avoid reflect overhead, use ClaimsFactory
// instead. Default: jwt.MapClaims{}.
Claims jwtparse.Claims
// ClaimsFactory creates a fresh Claims instance per request. When set,
// this takes precedence over the Claims template field. Use this for
// custom struct claims types to avoid data races.
ClaimsFactory func() jwtparse.Claims
// KeyFunc is a custom key function that overrides SigningKey/SigningKeys.
KeyFunc jwtparse.Keyfunc
// JWKSURL is a URL for JWKS auto-discovery (e.g. Auth0, Keycloak).
// When JWKSURLs is also set, this URL is prepended to that list.
JWKSURL string
// JWKSURLs is a list of JWKS URLs for multi-provider federation.
// When a token's kid is not found in one provider's keyset, the
// next URL's keys are tried in order. If JWKSURL is also set, it
// is treated as the first entry.
JWKSURLs []string
// JWKSRefresh is the interval for re-fetching the JWKS keyset.
// Default: 1h.
JWKSRefresh time.Duration
// JWKSPreload controls whether JWKS keys are fetched eagerly at
// middleware creation time. When nil (default) or pointing to true,
// Preload is enabled and the first request avoids a blocking network
// call. Set to a pointer to false to disable preloading (lazy fetch
// on first request). If preload fails, a warning is logged and the
// middleware falls back to lazy fetching.
JWKSPreload *bool
// TokenProcessorFunc is called on the raw token string after extraction
// but before parsing. Use it for decryption, decompression, or other
// transformations. If it returns an error, the middleware returns
// ErrTokenInvalid wrapping that error.
//
// The returned string must be a valid compact JWS (3-part
// dot-separated). Returning an empty string or a non-JWS value will
// cause a parse error. The original raw token is NOT preserved;
// Token.Raw will contain the transformed value.
TokenProcessorFunc func(token string) (string, error)
// TokenContextKey overrides the context store key for the parsed token.
// Default: "jwt_token" (TokenKey).
TokenContextKey string
// ClaimsContextKey overrides the context store key for the claims.
// Default: "jwt_claims" (ClaimsKey).
ClaimsContextKey string
// ParseOptions provides additional parser options (e.g., WithLeeway,
// WithIssuer, WithAudience) appended after the default ValidMethods
// option. This allows fine-grained control over the JWT parser without
// needing a custom KeyFunc.
ParseOptions []jwtparse.ParserOption
// ErrorHandler handles authentication failures. Receives the error
// and returns the error to propagate. Default: returns the error as-is.
ErrorHandler func(c *celeris.Context, err error) error
// SuccessHandler is called after successful token validation, before
// c.Next(). Use for logging, metrics, or enriching the context.
SuccessHandler func(c *celeris.Context)
// ContinueOnIgnoredError, when true, causes the middleware to call
// c.Next() when ErrorHandler returns nil. By default (false), when
// ErrorHandler returns nil the middleware returns nil immediately
// without calling c.Next(), short-circuiting the handler chain.
// Note: when false and ErrorHandler returns nil, the middleware
// itself returns nil (not the original error). The framework's
// outer handler loop then runs downstream handlers independently;
// errors from those handlers are NOT propagated through this
// middleware's return value. Enable ContinueOnIgnoredError to
// explicitly call c.Next() and propagate downstream errors.
ContinueOnIgnoredError bool
}
Config defines the JWT middleware configuration.
type NumericDate ¶
type NumericDate = jwtparse.NumericDate
NumericDate represents a JSON numeric date value (Unix timestamp).
func NewNumericDate ¶
func NewNumericDate(t time.Time) *NumericDate
NewNumericDate creates a NumericDate from a time.Time.
type ParserOption ¶
type ParserOption = jwtparse.ParserOption
ParserOption configures the JWT parser.
type RegisteredClaims ¶
type RegisteredClaims = jwtparse.RegisteredClaims
RegisteredClaims contains the registered (standard) JWT claim fields.
type SigningMethod ¶
type SigningMethod = jwtparse.SigningMethod
SigningMethod is the interface for JWT signing algorithms.