API Key Authentication
Regius includes an API key authentication middleware to authenticate API requests, complementing the existing session/cookie auth used by web routes.
Features
- Opt-in via
API_KEY_AUTH_ENABLED: Apply to API route groups only (not global) so cookie-authed web routes are unaffected - Multiple key sources (in order):
Authorization: Bearer <key>(or configured scheme),X-API-Keyheader, and an opt-in query param (API_KEY_QUERY_PARAM, disabled by default since keys in URLs leak via logs/referrers) - Flexible validation backends (by precedence): a pluggable
Validatorfunc (e.g. DB-backed keys), a cache-backedAPIKeyStore(with revocation), or a static list ofKeyscompared in constant time (crypto/subtle) - Cache-backed store:
CacheAPIKeyStoreadapts the framework cache (Redis/Badger); entries are keyed by the SHA-256 of the raw key, so raw keys are never persisted - Context propagation: On success, the
APIKeyIdentityis stored in the request context — retrieve it withregius.APIKeyFromContext(ctx) - Secure failure: On failure, returns
401withWWW-Authenticate,Cache-Control: no-store, and a JSON body. The raw key is never logged
Usage
Apply to your API route group (routes.go). API key auth is NOT global.
r.Group(func(mux chi.Router) {
mux.Use(a.APIKeyAuth(regius.APIKeyAuthConfig{
Enabled: true,
Keys: []string{"client-1-secret", "client-2-secret"},
}))
// API routes here...
})
Or use env-driven config (set API_KEY_AUTH_ENABLED=true and API_KEYS in .env):
mux.Use(a.APIKeyAuth(a.APIKeyAuthCfg()))
DB-backed keys via a custom validator:
mux.Use(a.APIKeyAuth(regius.APIKeyAuthConfig{
Enabled: true,
Validator: func(key string) (regius.APIKeyIdentity, bool) {
// look up key in DB; return identity if valid
return regius.APIKeyIdentity{ID: "user-42"}, true
},
}))
Retrieve the authenticated caller in a handler:
func (a *App) SomeAPIHandler(w http.ResponseWriter, r *http.Request) {
id, ok := regius.APIKeyFromContext(r.Context())
if !ok { /* unauthorized */ }
// use id.ID, id.Metadata...
}
Configuration Options
config := regius.APIKeyAuthConfig{
Enabled: true, // Master toggle
Keys: []string{"secret"}, // Static valid keys (constant-time compare)
Validator: nil, // Pluggable func(key) (identity, ok); takes precedence
Store: nil, // APIKeyStore (e.g. CacheAPIKeyStore) for lookup/revocation
Header: "Authorization", // Primary header (default "Authorization")
Scheme: "Bearer", // Scheme prefix for Header (default "Bearer")
AltHeader: "X-API-Key", // Secondary header, no prefix (default "X-API-Key")
QueryParam: "", // Opt-in query param name (default "" = off)
Realm: "api", // Used in WWW-Authenticate (default "api")
}
Cache-Backed Store
Keys are hashed with SHA-256 and never stored raw:
store := regius.NewCacheAPIKeyStore(a.Cache, "apikey:")
_ = store.Set("client-secret", regius.APIKeyIdentity{ID: "client-1"}, 0)
_ = store.Revoke("client-secret") // invalidate later
Environment Variables
API_KEY_AUTH_ENABLED=false
API_KEYS= # comma-separated list of valid keys
API_KEY_HEADER=Authorization
API_KEY_SCHEME=Bearer
API_KEY_ALT_HEADER=X-API-Key
API_KEY_QUERY_PARAM= # empty = disabled
API_KEY_REALM=api