Documentation
¶
Overview ¶
Package middleware provides HTTP middleware for the API server.
Index ¶
- Constants
- Variables
- func APICSRFToken(key []byte, cookieToken string) string
- func APIKeyAuthMiddleware(apiKeyService *auth.APIKeyService, rateLimiter *APIKeyRateLimiter) func(http.Handler) http.Handler
- func AuthMiddleware(validateBearer, validateCookie SessionValidator, cookieAuth bool) func(http.Handler) http.Handler
- func AuthOrAPIKey(v APIKeyValidator, limiter APIKeyAllower, next func(http.Handler) http.Handler) func(http.Handler) http.Handler
- func CSRF(key []byte) func(http.Handler) http.Handler
- func ClearCSRFCookie(w http.ResponseWriter, secure bool)
- func ContextWithClaims(ctx context.Context, claims *auth.Claims) context.Context
- func GetAPICookieToken(ctx context.Context) (string, bool)
- func GetAPIKey(ctx context.Context) (*auth.APIKey, bool)
- func GetClaims(ctx context.Context) (*auth.Claims, bool)
- func GetClientIP(r *http.Request) string
- func GetTenantID(ctx context.Context) (uuid.UUID, bool)
- func GetUserID(ctx context.Context) (uuid.UUID, bool)
- func IPBucket(ip string) string
- func IsTrustedProxy(remoteAddr string) bool
- func NewCSRFToken() string
- func RecoveryLogger(logger *slog.Logger, redact func(path string) string) func(http.Handler) http.Handler
- func RequestLogger(logger *slog.Logger, redact func(path string) string) func(http.Handler) http.Handler
- func RequireAPIKeyScope(scope auth.APIKeyScope) func(http.Handler) http.Handler
- func RequireRole(roles ...string) func(http.Handler) http.Handler
- func SetCSRFCookie(w http.ResponseWriter, secure bool) string
- func SetTrustedProxies(cidrs []string)
- func Trace(r *http.Request, tag string)
- func UseNeutralCSRFCookieName(secure bool)
- type APIKeyAllower
- type APIKeyRateLimiter
- type APIKeyValidator
- type AuthType
- type RateLimiter
- type SessionValidator
Constants ¶
const CSRFHeaderName = "X-CSRF-Token"
CSRF protects the COOKIE-authenticated JSON API (ENABLE_COOKIE_AUTH, default off; see server/handlers.go) with a synchronizer token bound to the API session cookie: the token is an HMAC of the cookie value under a server key. Page script cannot read the HttpOnly cookie, so it can neither compute nor fetch the token; login returns it in the response body. Unsafe cookie requests must also be same-origin per Sec-Fetch-Site and Origin. Requests that were not authenticated by the cookie (bearer, API key) are exempt: bearer auth is not CSRF-vulnerable.
Variables ¶
var CSRFCookieName = "hatamoto_csrf"
CSRFCookieName is the double-submit cookie name. Like auth.SessionCookieName it keeps the historical default and is switched once at boot.
var TraceHook func(r *http.Request, tag string)
TraceHook, when set, is called with a short tag each time a request enters one of the gates (auth, CSRF, role, rate limit; the server adds tenant and capture). Tests use it to pin which gates guard which route; production never sets it. It observes only: every gate still enforces.
Functions ¶
func APICSRFToken ¶
APICSRFToken returns the CSRF token bound to the API session cookie value.
func APIKeyAuthMiddleware ¶
func APIKeyAuthMiddleware(apiKeyService *auth.APIKeyService, rateLimiter *APIKeyRateLimiter) func(http.Handler) http.Handler
APIKeyAuthMiddleware validates API keys from the X-API-Key header. It sets tenant context similar to session auth, enabling RLS.
func AuthMiddleware ¶
func AuthMiddleware(validateBearer, validateCookie SessionValidator, cookieAuth bool) func(http.Handler) http.Handler
AuthMiddleware authenticates a request by its server-side session: the opaque token in Authorization: Bearer (checked by validateBearer), else (when cookieAuth) the HttpOnly API session cookie (checked by validateCookie, which must accept API-kind sessions only). The admin form session cookie is never read here. Anything else is a 401.
func AuthOrAPIKey ¶
func AuthOrAPIKey(v APIKeyValidator, limiter APIKeyAllower, next func(http.Handler) http.Handler) func(http.Handler) http.Handler
AuthOrAPIKey authenticates a request that carries an X-API-Key header as an API key, restricted to keyRoutes; requests without the header go to next (the regular session auth) unchanged. A request with both a header and a session cookie is key-authenticated.
Key requests run with synthesised editor claims for the key's creator (never superadmin) so existing handlers work unmodified, plus the API key and AuthTypeAPIKey in the context (so CSRF can be skipped and saves attributed).
func CSRF ¶
CSRF returns middleware enforcing the synchronizer check on unsafe methods when the request was authenticated via the API session cookie.
func ClearCSRFCookie ¶
func ClearCSRFCookie(w http.ResponseWriter, secure bool)
ClearCSRFCookie expires the CSRF cookie (logout). Legacy-named cookies are expired by the server's legacy cookie middleware.
func ContextWithClaims ¶
ContextWithClaims returns a copy of ctx carrying the given claims. Exposed so tests (and any in-process caller) can populate the same context slot the auth middleware uses, without importing the unexported context key.
func GetAPICookieToken ¶
GetAPICookieToken returns the API session cookie token the request was authenticated with. It is absent for bearer and API-key requests, which is what lets CSRF protect cookie-authenticated requests only.
func GetClientIP ¶
GetClientIP extracts the client IP from the request as a string. Honours X-Forwarded-For / X-Real-IP only when the immediate RemoteAddr is in the trusted-proxy CIDR list. Exported so other packages can share a single trust policy.
func GetTenantID ¶
GetTenantID is retained for callers that historically asked "is the current request authenticated as a tenant principal?". Post-Path-B the tenant dimension is gone; the second return value is the only useful signal. Returns (uuid.Nil, true) when the request carries an admin claim or API key, (uuid.Nil, false) otherwise.
func IPBucket ¶
IPBucket is the key a client address is limited under: the address itself, or for IPv6 its /64, so one host with a whole prefix gets one bucket, not 2^64.
func IsTrustedProxy ¶
IsTrustedProxy reports whether remoteAddr (host:port or bare IP) is a trusted proxy, i.e. whether its X-Forwarded-* headers may be believed.
func RecoveryLogger ¶
func RecoveryLogger(logger *slog.Logger, redact func(path string) string) func(http.Handler) http.Handler
RecoveryLogger recovers from panics and logs them. redact works as in RequestLogger.
func RequestLogger ¶
func RequestLogger(logger *slog.Logger, redact func(path string) string) func(http.Handler) http.Handler
RequestLogger logs HTTP requests with structured logging. redact maps the request path to the string that is logged (nil logs it unchanged); it runs before the handler so a prefix swapped mid-request is still the one redacted.
func RequireAPIKeyScope ¶
RequireAPIKeyScope middleware ensures the API key has the required scope.
func RequireRole ¶
RequireRole middleware ensures the user has one of the specified roles.
func SetCSRFCookie ¶
func SetCSRFCookie(w http.ResponseWriter, secure bool) string
SetCSRFCookie mints a token and sets it as a NON-HttpOnly cookie (the SPA must read it to echo in the header). SameSite=Strict, Secure configurable. Returns the token so callers may also surface it in a response body if desired.
func SetTrustedProxies ¶
func SetTrustedProxies(cidrs []string)
SetTrustedProxies replaces the trusted-proxy CIDR list (HATAMOTO_TRUSTED_PROXIES). Call it once at startup, before serving; an empty list keeps the loopback-only default.
func UseNeutralCSRFCookieName ¶
func UseNeutralCSRFCookieName(secure bool)
UseNeutralCSRFCookieName sets "__Host-c" when secure, "c" otherwise (see auth.UseNeutralCookieNames for why plain-HTTP dev cannot use the prefix).
Types ¶
type APIKeyAllower ¶
APIKeyAllower is the slice of APIKeyRateLimiter that AuthOrAPIKey needs.
type APIKeyRateLimiter ¶
type APIKeyRateLimiter struct {
// contains filtered or unexported fields
}
APIKeyRateLimiter implements per-key rate limiting for API keys.
func NewAPIKeyRateLimiter ¶
func NewAPIKeyRateLimiter() *APIKeyRateLimiter
NewAPIKeyRateLimiter creates a new API key rate limiter.
func (*APIKeyRateLimiter) Allow ¶
func (rl *APIKeyRateLimiter) Allow(keyID uuid.UUID, rateLimit int) bool
Allow checks if a request is allowed for the given API key. Returns true if allowed, false if rate limited.
func (*APIKeyRateLimiter) Stop ¶
func (rl *APIKeyRateLimiter) Stop()
Stop terminates the background cleanup goroutine.
type APIKeyValidator ¶
type APIKeyValidator interface {
ValidateKey(ctx context.Context, plainKey string) (*auth.APIKey, error)
RecordUsage(ctx context.Context, keyID uuid.UUID, ipAddress string) error
}
APIKeyValidator is the slice of auth.APIKeyService that AuthOrAPIKey needs.
type AuthType ¶
type AuthType string
AuthType represents the type of authentication used.
const (
AuthTypeAPIKey AuthType = "api_key"
)
func GetAuthType ¶
GetAuthType returns the authentication type used for the request.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter implements per-IP rate limiting.
func APIRateLimiter ¶
func APIRateLimiter() *RateLimiter
APIRateLimiter is a general rate limiter for API endpoints.
func AuthRateLimiter ¶
func AuthRateLimiter() *RateLimiter
AuthRateLimiter is a stricter rate limiter for auth endpoints.
func NewRateLimiter ¶
func NewRateLimiter(rps float64, burst int) *RateLimiter
NewRateLimiter creates a new rate limiter. rate: requests per second burst: maximum burst size
func (*RateLimiter) Allow ¶
func (rl *RateLimiter) Allow(key string) bool
Allow reports whether key may make a request now; it is the limiter's own bucket logic for callers that choose the key themselves (a session, say).
func (*RateLimiter) ByIPv6Prefix ¶
func (rl *RateLimiter) ByIPv6Prefix() *RateLimiter
ByIPv6Prefix makes the limiter count an IPv6 client by its /64 instead of its single address (see IPBucket) and returns it. Used where a host with a whole prefix must not get a bucket per address, such as the public demo.
func (*RateLimiter) Middleware ¶
func (rl *RateLimiter) Middleware(next http.Handler) http.Handler
Middleware returns a rate limiting middleware.
func (*RateLimiter) Stop ¶
func (rl *RateLimiter) Stop()
Stop terminates the background cleanup goroutine.