Documentation
¶
Overview ¶
Package keyauth provides API key authentication middleware for celeris.
The middleware extracts an API key from a configurable source (header, query parameter, cookie, form field, or URL parameter), validates it via a user-supplied function, and stores the authenticated key in the context store under ContextKey ("keyauth_key"). Failed authentication returns 401 with a WWW-Authenticate header.
Config.Validator is required; omitting it panics at initialization.
Simple usage with static keys (constant-time comparison):
server.Use(keyauth.New(keyauth.Config{
Validator: keyauth.StaticKeys("key-1", "key-2"),
}))
Custom validator with context access:
server.Use(keyauth.New(keyauth.Config{
Validator: func(c *celeris.Context, key string) (bool, error) {
return checkDB(c, key)
},
KeyLookup: "query:api_key",
}))
Multiple sources can be comma-separated for fallback:
KeyLookup: "header:Authorization:Bearer ,query:api_key"
Use KeyFromContext to retrieve the authenticated key downstream.
Config.ChallengeParams controls RFC 6750 parameters in the WWW-Authenticate header (error, error_description, error_uri, scope).
StaticKeys uses constant-time comparison via crypto/subtle to prevent timing side-channels.
ErrUnauthorized and ErrMissingKey are exported sentinel errors (both 401) usable with errors.Is for upstream error handling.
Set Config.Skip to bypass the middleware dynamically, or Config.SkipPaths for exact-match path exclusions.
Security: API keys in query strings (`query:api_key`) are logged in access logs, browser history, and Referer headers. Prefer header-based key lookup for sensitive environments.
Index ¶
Examples ¶
Constants ¶
const ContextKey = "keyauth_key"
ContextKey is the context store key for the authenticated API key.
Variables ¶
var ErrMissingKey = &celeris.HTTPError{Code: 401, Message: "Unauthorized", Err: errors.New("keyauth: missing API key")}
ErrMissingKey is returned when no API key is found in the request. Do not mutate: this is a shared sentinel value used with errors.Is.
ErrUnauthorized is returned when the API key is invalid. Do not mutate: this is a shared sentinel value used with errors.Is.
Functions ¶
func KeyFromContext ¶
KeyFromContext returns the authenticated API key from the context store. Returns an empty string if no key was stored (e.g., no auth or skipped).
func New ¶
func New(config ...Config) celeris.HandlerFunc
New creates a key auth middleware with the given config.
Example ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Static API keys with constant-time comparison.
_ = keyauth.New(keyauth.Config{
Validator: keyauth.StaticKeys("key-1", "key-2"),
})
}
Output:
Example (Cookie) ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Extract key from a cookie.
_ = keyauth.New(keyauth.Config{
KeyLookup: "cookie:auth_token",
Validator: keyauth.StaticKeys("token-value"),
})
}
Output:
Example (CustomValidator) ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Custom validator with context access.
_ = keyauth.New(keyauth.Config{
Validator: func(c *celeris.Context, key string) (bool, error) {
tenant := c.Header("x-tenant")
_ = tenant // look up key in tenant-specific store
return key == "expected", nil
},
})
}
Output:
Example (ErrorHandler) ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Custom error handler for JSON responses.
_ = keyauth.New(keyauth.Config{
Validator: keyauth.StaticKeys("secret"),
ErrorHandler: func(c *celeris.Context, err error) error {
return c.JSON(401, map[string]string{"error": err.Error()})
},
})
}
Output:
Example (FormField) ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Extract key from a form field (URL-encoded or multipart).
_ = keyauth.New(keyauth.Config{
KeyLookup: "form:api_key",
Validator: keyauth.StaticKeys("form-secret"),
})
}
Output:
Example (QueryParam) ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Extract key from query parameter instead of header.
_ = keyauth.New(keyauth.Config{
KeyLookup: "query:api_key",
Validator: keyauth.StaticKeys("my-secret-key"),
})
}
Output:
Example (SuccessHandler) ¶
package main
import (
"github.com/goceleris/celeris"
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Enrich context after successful auth.
_ = keyauth.New(keyauth.Config{
Validator: keyauth.StaticKeys("admin-key"),
SuccessHandler: func(c *celeris.Context) {
c.Set("role", "admin")
},
})
}
Output:
Example (UrlParam) ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Extract key from a URL path parameter (e.g., /api/:token/data).
_ = keyauth.New(keyauth.Config{
KeyLookup: "param:token",
Validator: keyauth.StaticKeys("param-secret"),
})
}
Output:
Example (WwwAuthenticate) ¶
package main
import (
"github.com/goceleris/celeris/middleware/keyauth"
)
func main() {
// Custom WWW-Authenticate header with Bearer scheme.
_ = keyauth.New(keyauth.Config{
Validator: keyauth.StaticKeys("secret"),
AuthScheme: "Bearer",
Realm: "MyAPI",
})
}
Output:
func StaticKeys ¶
StaticKeys returns a constant-time validator that checks the key against a fixed set of valid keys. All comparisons use a uniform code path to prevent timing side-channels on key existence or length.
Duplicate keys in the input are not deduplicated; each copy is compared independently. This has no effect on correctness.
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
// KeyLookup defines where to extract the API key.
//
// Format: "source:name" or "source:name:prefix" for prefix stripping.
// Multiple sources can be comma-separated for fallback, e.g.
// "header:Authorization:Bearer ,query:api_key".
//
// Supported sources: header, query, cookie, form, param.
// Default: "header:X-API-Key".
KeyLookup string
// Validator checks the extracted key. Required -- panics if nil.
//
// Implementations SHOULD use crypto/subtle.ConstantTimeCompare or
// equivalent to prevent timing attacks. See StaticKeys for a
// ready-made constant-time validator.
Validator func(c *celeris.Context, key string) (bool, error)
// SuccessHandler is called after a key is successfully validated,
// before c.Next(). Use it to enrich the context (e.g., set tenant ID
// based on key). If nil, no extra action is taken.
SuccessHandler func(c *celeris.Context)
// ErrorHandler handles authentication failures. Receives the error
// (ErrMissingKey or ErrUnauthorized) and returns the error to propagate.
// Default: returns the error as-is.
ErrorHandler func(c *celeris.Context, err error) error
// Realm is the realm value used in the WWW-Authenticate header on 401.
// Default: "Restricted".
Realm string
// AuthScheme is the authentication scheme used in the WWW-Authenticate
// header (e.g. "Bearer"). When empty, "ApiKey" is used as the scheme.
AuthScheme string
// ChallengeParams is a map of RFC 6750 parameters for the
// WWW-Authenticate header. Common keys: "error", "error_description",
// "error_uri", "scope". Values are emitted as quoted-string parameters
// in sorted key order.
//
// The "error" value must be one of "invalid_request", "invalid_token",
// or "insufficient_scope". "error_uri" must be a valid absolute URI.
// "scope" tokens must be valid NQCHAR per RFC 6749 Appendix A.
ChallengeParams map[string]string
// ContinueOnIgnoredError, when true, allows the request to proceed
// if ErrorHandler returns nil. This enables mixed public/private routes
// where authentication is optional.
ContinueOnIgnoredError bool
}
Config defines the key auth middleware configuration.