Documentation
¶
Overview ¶
Package ephemeralauth provides stateless short-lived JWT gating for public API endpoints. It includes guest-session middleware, token issuance with bot verification, and a protection middleware — all gorilla/mux-compatible (func(http.Handler) http.Handler) and usable standalone or auto-wired through litespaserver config.
Index ¶
- Constants
- func DeriveKey(raw []byte) []byte
- func GuestSession(signingKey []byte) func(http.Handler) http.Handler
- func GuestSessionID(r *http.Request, signingKey []byte) (string, bool)
- func HashContext(key []byte, value string) string
- func IssuanceHandler(cfg Config, verifier BotVerifier, issuer *Issuer) http.Handler
- func Protect(issuer *Issuer, trustProxy bool, requiredScopes ...string) func(http.Handler) http.Handler
- type BotVerifier
- type Claims
- type Config
- type Issuer
- type TurnstileResult
- type TurnstileVerifier
Constants ¶
const ( // TurnstileTestAlwaysPassSitekey always triggers a passing challenge. TurnstileTestAlwaysPassSitekey = "1x00000000000000000000AA" // TurnstileTestAlwaysPassSecret always returns success: true. TurnstileTestAlwaysPassSecret = "1x0000000000000000000000000000000AA" // TurnstileTestAlwaysFailSitekey always triggers a failing challenge. TurnstileTestAlwaysFailSitekey = "2x00000000000000000000AB" // TurnstileTestAlwaysFailSecret always returns success: false. TurnstileTestAlwaysFailSecret = "2x0000000000000000000000000000000AA" // TurnstileTestForcesChallengeSitekey forces an interactive challenge. // No automated integration test exists — this key requires human // interaction to solve the challenge in a browser. TurnstileTestForcesChallengeSitekey = "3x00000000000000000000FF" // TurnstileTestTokenExpiredSecret returns a timeout-or-duplicate error. TurnstileTestTokenExpiredSecret = "3x0000000000000000000000000000000AA" )
Cloudflare Turnstile official dummy test keys for development and testing. See: https://developers.cloudflare.com/turnstile/troubleshooting/testing/
Variables ¶
This section is empty.
Functions ¶
func DeriveKey ¶ added in v0.7.1
DeriveKey normalises an arbitrary-length signing key to exactly 32 bytes. Keys that are already 32 bytes are returned as-is (backward compatible with deployments that predate DeriveKey). Shorter or longer keys are hashed with SHA-256.
All public functions in this package call DeriveKey internally (some once at setup, others per call), so consumers may supply any non-empty key.
WARNING: The derived key must only be used with this package's JWT and context-binding operations. Using it for other cryptographic purposes breaks key isolation.
Panics if raw is nil or empty.
func GuestSession ¶
GuestSession returns middleware that ensures every request carries a valid HMAC-signed guest session cookie. If no valid cookie is present, a new one is generated and set via Set-Cookie. The signing key is normalised to 32 bytes via DeriveKey internally.
func GuestSessionID ¶
GuestSessionID extracts and verifies the guest session cookie from the request. Returns the session ID and true if valid, empty string and false otherwise. The signing key is normalised to 32 bytes via DeriveKey internally.
func HashContext ¶
HashContext computes HMAC-SHA256(DeriveKey(key), value), truncated to 16 bytes, base64url-encoded. Used for IP and User-Agent context binding.
func IssuanceHandler ¶
func IssuanceHandler(cfg Config, verifier BotVerifier, issuer *Issuer) http.Handler
IssuanceHandler returns an http.Handler for the ephemeral token issuance endpoint (POST /api/auth/ephemeral-token).
func Protect ¶
func Protect(issuer *Issuer, trustProxy bool, requiredScopes ...string) func(http.Handler) http.Handler
Protect returns middleware that protects API routes with ephemeral token verification. It extracts the Bearer token, verifies its signature and expiry, checks context binding (session, IP, UA), and validates scopes.
Missing/malformed header → 401 Expired or bad-signature → 401 Binding mismatch (session/IP/UA) → 403 Insufficient scope → 403 Valid + bound + scoped → request passes through
Types ¶
type BotVerifier ¶
type BotVerifier interface {
Verify(ctx context.Context, token string, remoteIP string) (bool, error)
}
BotVerifier verifies bot-protection tokens from the client. Implementations must be fail-closed: return (false, err) or (false, nil) to reject; (true, nil) to accept.
type Claims ¶
type Claims struct {
jwt.RegisteredClaims
IPHash string `json:"ip_hash"`
UAHash string `json:"ua_hash"`
Scopes []string `json:"scopes"`
}
Claims extends jwt.RegisteredClaims with context-binding fields.
type Config ¶
type Config struct {
// SigningKey is the HMAC-SHA256 key used for JWT signing and guest
// cookie HMAC. Required. Any non-empty value is accepted; it is
// normalised to 32 bytes via SHA-256 (DeriveKey).
// Env: SIGNING_KEY (prefix supplied by embedding struct).
SigningKey string `env:"SIGNING_KEY"`
// TokenTTLSeconds is the token lifetime in seconds. Clamped to [60, 180].
// Default: 120. Env: TOKEN_TTL_SECONDS.
TokenTTLSeconds int `env:"TOKEN_TTL_SECONDS" envDefault:"120"`
// TurnstileSecret is the Cloudflare Turnstile secret for the default
// BotVerifier implementation. If empty, the consumer must supply a
// BotVerifier. Env: TURNSTILE_SECRET.
TurnstileSecret string `env:"TURNSTILE_SECRET"`
// TurnstileSitekey is the Cloudflare Turnstile sitekey for the
// frontend widget. This is a public key (not secret) that the
// frontend uses to render the challenge. Env: TURNSTILE_SITEKEY.
TurnstileSitekey string `env:"TURNSTILE_SITEKEY"`
// TrustProxy enables reading X-Forwarded-For for client IP extraction.
// Default: false (use r.RemoteAddr). Env: TRUST_PROXY.
TrustProxy bool `env:"TRUST_PROXY" envDefault:"false"`
// Scopes is the default scope list issued to new tokens.
// Default: ["public:read"]. Env: SCOPES.
Scopes []string `env:"SCOPES" envDefault:"public:read"`
// RequiredScopes is the list of scopes that must be present in a token
// for the protection middleware to allow the request through. When empty,
// any valid token is accepted. Env: REQUIRED_SCOPES.
RequiredScopes []string `env:"REQUIRED_SCOPES"`
}
Config holds all configuration for the ephemeralauth package. Struct tags follow the caarlos0/env convention. The env key names are intentionally prefix-free so that Config can be embedded in a parent struct with an envPrefix tag:
type AppConfig struct {
EphemeralAuth ephemeralauth.Config `envPrefix:"EPHEMERAL_"`
}
With the prefix above, the effective env var for SigningKey becomes EPHEMERAL_SIGNING_KEY.
type Issuer ¶
type Issuer struct {
// contains filtered or unexported fields
}
Issuer creates and verifies ephemeral JWTs.
func NewIssuer ¶
NewIssuer creates an Issuer from the signing key and TTL. Any non-empty key is accepted; it is normalised to 32 bytes via DeriveKey (SHA-256). Keys shorter than 32 bytes produce a warning because they may offer insufficient entropy. ttl is clamped to [60s, 180s].
type TurnstileResult ¶
type TurnstileResult struct {
// Success indicates whether the challenge was solved correctly.
Success bool
// ErrorCodes contains error codes returned by Cloudflare (e.g.
// "timeout-or-duplicate", "invalid-input-secret").
ErrorCodes []string
}
TurnstileResult holds the detailed outcome of a Turnstile verification.
type TurnstileVerifier ¶
type TurnstileVerifier struct {
// contains filtered or unexported fields
}
TurnstileVerifier implements BotVerifier using Cloudflare Turnstile.
func NewTurnstileVerifier ¶
func NewTurnstileVerifier(secret string) *TurnstileVerifier
NewTurnstileVerifier creates a TurnstileVerifier with the given secret. Uses the default endpoint and a 10s timeout.
func (*TurnstileVerifier) Verify ¶
func (tv *TurnstileVerifier) Verify(ctx context.Context, token string, remoteIP string) (bool, error)
Verify implements BotVerifier. Fail-closed: any error or non-success response returns (false, error). Delegates to VerifyWithDetails.
func (*TurnstileVerifier) VerifyWithDetails ¶
func (tv *TurnstileVerifier) VerifyWithDetails(ctx context.Context, token string, remoteIP string) (*TurnstileResult, error)
VerifyWithDetails verifies the Turnstile token and returns the full result including any error codes from Cloudflare. Fail-closed: any HTTP or parsing error returns a non-nil error.