ephemeralauth

package
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 16 Imported by: 0

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

View Source
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

func DeriveKey(raw []byte) []byte

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

func GuestSession(signingKey []byte) func(http.Handler) http.Handler

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

func GuestSessionID(r *http.Request, signingKey []byte) (string, bool)

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

func HashContext(key []byte, value string) string

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.

func (Config) Issuer

func (c Config) Issuer() *Issuer

Issuer creates an Issuer from this config.

func (Config) LogValue

func (c Config) LogValue() slog.Value

LogValue implements slog.LogValuer, redacting the signing key and Turnstile secret.

func (Config) TTL

func (c Config) TTL() time.Duration

TTL returns the configured TTL as a time.Duration, clamped to [60s, 180s].

type Issuer

type Issuer struct {
	// contains filtered or unexported fields
}

Issuer creates and verifies ephemeral JWTs.

func NewIssuer

func NewIssuer(signingKey []byte, ttl time.Duration) *Issuer

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].

func (*Issuer) Issue

func (iss *Issuer) Issue(sessionID, ipHash, uaHash string, scopes []string) (string, int, error)

Issue creates a signed JWT with the provided context bindings. Returns the token string, the expires_in seconds, and any error.

func (*Issuer) Verify

func (iss *Issuer) Verify(tokenString string) (*Claims, error)

Verify parses and validates the token. Algorithm is pinned to HS256. Returns the claims on success or an error describing the failure.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL