Documentation
¶
Overview ¶
Package middleware provides HTTP middleware for the HAMR framework.
Index ¶
- Constants
- Variables
- func Audit(logger AuditLogger) echo.MiddlewareFunc
- func AuditWithConfig(cfg AuditConfig) echo.MiddlewareFunc
- func CORS() echo.MiddlewareFunc
- func CORSWithConfig(cfg CORSConfig) echo.MiddlewareFunc
- func CSRF() echo.MiddlewareFunc
- func CSRFWithConfig(cfg CSRFConfig) echo.MiddlewareFunc
- func CacheControl(disableCaching bool) echo.MiddlewareFunc
- func CacheControlWithConfig(cfg CacheConfig) echo.MiddlewareFunc
- func ErrorPages(defaultPage ErrorPage, overrides ...PageOverride) echo.MiddlewareFunc
- func Flash() echo.MiddlewareFunc
- func FlashWithConfig(cfg FlashConfig) echo.MiddlewareFunc
- func GetDirection(c echo.Context) string
- func GetLocale(c echo.Context) string
- func GetSubject(c echo.Context) any
- func GetSubjectID(c echo.Context) string
- func LocaleFromPath(cfg LocaleConfig) echo.MiddlewareFunc
- func LocaleFromPreference(cfg LocaleConfig) echo.MiddlewareFunc
- func RateLimit(store RateLimitStore) echo.MiddlewareFunc
- func RateLimitWithConfig(cfg RateLimitConfig) echo.MiddlewareFunc
- func RequireActive(checker ActiveChecker) echo.MiddlewareFunc
- func RequireRoles(checker RoleChecker, roles ...string) echo.MiddlewareFunc
- func Secure() echo.MiddlewareFunc
- func SecureWithConfig(cfg SecureConfig) echo.MiddlewareFunc
- func SetFlash(c echo.Context, message string, flashType FlashType)
- func TrustedSubject() echo.MiddlewareFunc
- func TrustedSubjectWithConfig(cfg TrustedSubjectConfig) echo.MiddlewareFunc
- type ActiveChecker
- type AuditConfig
- type AuditEntry
- type AuditLogger
- type BrowserAuth
- type BrowserAuthOption
- type CORSConfig
- type CSRFConfig
- type CacheConfig
- type DB
- type ErrorPage
- type FlashConfig
- type FlashMessage
- type FlashType
- type LocaleConfig
- type MemoryStore
- type MemoryStoreOption
- type PGStore
- type PageOverride
- type RateLimitConfig
- type RateLimitStore
- type RoleChecker
- type SecureConfig
- type SubjectLoader
- type TrustedSubjectConfig
Constants ¶
const FlashCookieName = "hamr_flash"
FlashCookieName is the cookie name used for flash messages.
Variables ¶
var DefaultImmutableExtensions = []string{
".webp", ".jpg", ".jpeg", ".png", ".gif", ".svg", ".ico",
".woff2", ".woff", ".ttf", ".eot",
}
DefaultImmutableExtensions are file extensions treated as immutable assets.
var DefaultStaticExtensions = []string{".css", ".js"}
DefaultStaticExtensions are file extensions treated as cacheable static assets.
Functions ¶
func Audit ¶
func Audit(logger AuditLogger) echo.MiddlewareFunc
Audit returns middleware that logs non-GET mutations via the given logger.
func AuditWithConfig ¶
func AuditWithConfig(cfg AuditConfig) echo.MiddlewareFunc
AuditWithConfig returns audit middleware with the given config.
func CORSWithConfig ¶
func CORSWithConfig(cfg CORSConfig) echo.MiddlewareFunc
CORSWithConfig returns CORS middleware with the given config.
When AllowOrigins is empty, cross-origin requests are DENIED (no Access-Control-Allow-Origin header) rather than falling back to Echo's permissive "*" default — apps that need CORS must pass explicit origins.
func CSRF ¶
func CSRF() echo.MiddlewareFunc
CSRF returns CSRF protection middleware with framework defaults.
func CSRFWithConfig ¶
func CSRFWithConfig(cfg CSRFConfig) echo.MiddlewareFunc
CSRFWithConfig returns CSRF protection middleware with the given config.
func CacheControl ¶
func CacheControl(disableCaching bool) echo.MiddlewareFunc
CacheControl sets Cache-Control headers based on asset type. When disableCaching is true every response gets no-cache directives.
func CacheControlWithConfig ¶
func CacheControlWithConfig(cfg CacheConfig) echo.MiddlewareFunc
CacheControlWithConfig sets Cache-Control headers using the given config.
func ErrorPages ¶
func ErrorPages(defaultPage ErrorPage, overrides ...PageOverride) echo.MiddlewareFunc
ErrorPages returns middleware that catches errors and renders error pages. The default ErrorPage handles all codes unless overridden by Page() entries.
func Flash ¶
func Flash() echo.MiddlewareFunc
Flash reads a flash cookie from the request, stores it in the context, and clears the cookie so it is only shown once.
func FlashWithConfig ¶
func FlashWithConfig(cfg FlashConfig) echo.MiddlewareFunc
FlashWithConfig returns flash middleware with the given config.
func GetDirection ¶
GetDirection is a convenience helper that returns the text direction from the context's translator.
func GetSubject ¶
GetSubject returns the loaded subject from the request context. Only populated when a SubjectLoader is configured (session-based auth). Returns nil if no subject is loaded.
func GetSubjectID ¶
GetSubjectID returns the authenticated subject's ID from the request context. Works with both session-based auth (Auth middleware) and trusted header auth (TrustedSubject middleware). Returns empty string if no subject is set.
func LocaleFromPath ¶
func LocaleFromPath(cfg LocaleConfig) echo.MiddlewareFunc
LocaleFromPath returns pre-router middleware that extracts the locale from the first URL path segment. Used for public/SEO pages where the locale appears in the URL (e.g. /en/about, /fr/contact).
IMPORTANT: Register with e.Pre(), not e.Use(), because the locale prefix must be stripped before the Echo router matches a route:
e.Pre(middleware.LocaleFromPath(cfg))
If the first segment is a supported locale, it is stripped from the path and the translator is injected into the context. If no valid locale prefix is found, the request is redirected to /{defaultLocale}/... with a 301 (GET) or 307 (POST).
func LocaleFromPreference ¶
func LocaleFromPreference(cfg LocaleConfig) echo.MiddlewareFunc
LocaleFromPreference returns middleware that resolves the locale from user preferences: UserLocaleFunc > cookie > Accept-Language header > default. Used for authenticated/dashboard pages.
func RateLimit ¶
func RateLimit(store RateLimitStore) echo.MiddlewareFunc
RateLimit returns rate limiting middleware using the given store with default settings (60 req/min + 10 burst, fail-open).
func RateLimitWithConfig ¶
func RateLimitWithConfig(cfg RateLimitConfig) echo.MiddlewareFunc
RateLimitWithConfig returns rate limiting middleware with the given config.
func RequireActive ¶
func RequireActive(checker ActiveChecker) echo.MiddlewareFunc
RequireActive returns middleware that checks whether the authenticated subject's account is active. Returns 401 if no subject is present or 403 if the account is not active.
func RequireRoles ¶
func RequireRoles(checker RoleChecker, roles ...string) echo.MiddlewareFunc
RequireRoles returns middleware that checks whether the authenticated subject has one of the required roles. Returns 401 if no subject is present or 403 if the role check fails.
func Secure ¶
func Secure() echo.MiddlewareFunc
Secure returns security headers middleware with framework defaults.
func SecureWithConfig ¶
func SecureWithConfig(cfg SecureConfig) echo.MiddlewareFunc
SecureWithConfig returns security headers middleware with the given config.
func SetFlash ¶
SetFlash stores a flash message in a cookie for the next request. Uses the cookie policy from Flash middleware if present, otherwise falls back to secure defaults.
func TrustedSubject ¶
func TrustedSubject() echo.MiddlewareFunc
TrustedSubject reads X-Subject-ID and sets it as the subject identity with NO verification — equivalent to TrustedSubjectWithConfig(TrustedSubjectConfig{}). Use only behind a gateway on a trusted internal network. Prefer TrustedSubjectWithConfig with a shared secret and/or trusted CIDRs whenever the mount could be reached by untrusted clients; otherwise a single spoofed header is a full authentication bypass.
trusted := middleware.TrustedSubject()
api.GET("/billing", billingHandler.Get, trusted)
func TrustedSubjectWithConfig ¶
func TrustedSubjectWithConfig(cfg TrustedSubjectConfig) echo.MiddlewareFunc
TrustedSubjectWithConfig is TrustedSubject gated by a shared secret and/or trusted source CIDRs. When a gate is configured and the request fails it, the X-Subject-ID header is ignored (the subject is left unset) so downstream authorization fails closed.
Types ¶
type ActiveChecker ¶
ActiveChecker returns true if the subject's account is active.
type AuditConfig ¶
type AuditConfig struct {
Logger AuditLogger
ActorIDFunc func(c echo.Context) string // default: GetSubjectID
}
AuditConfig configures audit middleware.
type AuditEntry ¶
type AuditEntry struct {
ActorID string `json:"actor_id"`
Action string `json:"action"` // HTTP method
EntityType string `json:"entity_type"` // route path pattern
Data map[string]any `json:"data"`
Timestamp time.Time `json:"timestamp"`
}
AuditEntry records a single auditable action.
type AuditLogger ¶
type AuditLogger interface {
Log(ctx context.Context, entry *AuditEntry) error
}
AuditLogger persists audit entries. Projects implement this interface to store entries in their database, log aggregator, etc.
type BrowserAuth ¶
type BrowserAuth struct {
// contains filtered or unexported fields
}
BrowserAuth provides session-based authentication middleware split into a loader (DB access) and pure policy checks (ctx-only).
func NewBrowserAuth ¶
func NewBrowserAuth(sm *auth.SessionManager, opts ...BrowserAuthOption) *BrowserAuth
NewBrowserAuth creates a BrowserAuth with the given session manager and options.
func (*BrowserAuth) Load ¶
func (b *BrowserAuth) Load() echo.MiddlewareFunc
Load returns middleware that validates the session cookie and populates the Echo context. This is the only middleware that touches the database.
Behavior:
- No cookie → next (no subject in ctx)
- Cookie present, invalid/expired → clear cookie, next (no subject in ctx)
- DB error on ValidateSession or SubjectLoader → return error (500)
- Valid session → set SubjectIDKey, SessionKey, SubjectKey in ctx, enrich logger
func (*BrowserAuth) RequireAuth ¶
func (b *BrowserAuth) RequireAuth() echo.MiddlewareFunc
RequireAuth returns middleware that requires an authenticated subject in the context. Must be mounted after Load(). No database calls.
No subject in ctx → redirect to LoginRedirect (303 or HX-Redirect).
func (*BrowserAuth) RequireNotAuth ¶
func (b *BrowserAuth) RequireNotAuth() echo.MiddlewareFunc
RequireNotAuth returns middleware that redirects already-authenticated users away (e.g. from login/register pages). Must be mounted after Load(). No database calls.
Subject in ctx → redirect to HomeRedirect (303 or HX-Redirect).
type BrowserAuthOption ¶
type BrowserAuthOption func(*BrowserAuth)
BrowserAuthOption configures a BrowserAuth instance.
func WithHXRedirect ¶
func WithHXRedirect() BrowserAuthOption
WithHXRedirect makes policy middleware respond with an HX-Redirect header instead of a 303 Location redirect, for HTMX-driven navigations.
func WithHomeRedirect ¶
func WithHomeRedirect(url string) BrowserAuthOption
WithHomeRedirect sets the URL authenticated users are redirected away to (e.g. from login/register pages). Default: "/dashboard".
func WithLoginRedirect ¶
func WithLoginRedirect(url string) BrowserAuthOption
WithLoginRedirect sets the URL unauthenticated users are redirected to. Default: "/login".
func WithSubjectLoader ¶
func WithSubjectLoader(loader SubjectLoader) BrowserAuthOption
WithSubjectLoader sets a function that loads the full subject by ID. If not set, only SubjectIDKey and SessionKey are populated in context.
type CORSConfig ¶
type CORSConfig struct {
AllowOrigins []string
AllowMethods []string
AllowHeaders []string
AllowCredentials bool
}
CORSConfig allows overriding CORS defaults.
type CSRFConfig ¶
type CSRFConfig struct {
CookieName string // default: "csrf"
TokenLookup string // default: "form:csrf_token,header:X-CSRF-Token"
Secure bool // default: true
// SameSite controls the cookie's SameSite attribute. Zero value defaults
// to Lax (a sensible CSRF defense-in-depth) rather than the browser
// default. Set explicitly to override (e.g. http.SameSiteStrictMode).
SameSite http.SameSite
}
CSRFConfig allows overriding CSRF defaults.
type CacheConfig ¶
type CacheConfig struct {
// ImmutableExtensions are file extensions cached as immutable.
// Default: DefaultImmutableExtensions.
ImmutableExtensions []string
// ImmutableMaxAge is the max-age in seconds for immutable assets.
// Default: 31536000 (1 year).
ImmutableMaxAge int
// StaticExtensions are file extensions cached with a shorter TTL.
// Default: DefaultStaticExtensions.
StaticExtensions []string
// StaticMaxAge is the max-age in seconds for static assets.
// Default: 86400 (1 day).
StaticMaxAge int
// DisableCaching sets no-cache directives on every response.
DisableCaching bool
// AllowDynamicCaching disables the default "no-store, private" header on
// dynamic (non-static) responses. Leave false (default) so authenticated
// pages aren't retained by the browser back-button or shared proxy caches;
// set true for apps that serve cacheable public dynamic content. A handler
// can always override per-route by setting its own Cache-Control.
AllowDynamicCaching bool
}
CacheConfig configures the CacheControlWithConfig middleware.
type DB ¶
type DB interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}
DB is the minimal database interface satisfied by *sql.DB and *sqlx.DB.
type FlashConfig ¶
FlashConfig configures flash cookie behaviour.
type FlashMessage ¶
FlashMessage is a one-time message shown to the user after a redirect.
func GetFlash ¶
func GetFlash(c echo.Context) *FlashMessage
GetFlash returns the flash message from the context, or nil if none is set.
SECURITY: the flash cookie is base64-encoded only, NOT authenticated (no MAC), so Message and Type are attacker-influenceable — treat them as UNTRUSTED when rendering. They are safe interpolated through templ (which auto-escapes); do not render them via templ.Raw/unescaped HTML, and validate Type before using it to select a code path.
type LocaleConfig ¶
type LocaleConfig struct {
Bundle *i18n.Bundle
CookieName string // default: "hamr_locale"
CookieMaxAge int // default: 31536000 (1 year)
CookieSecure bool
DefaultLocale string // from bundle if empty
UserLocaleFunc func(c echo.Context) string // optional: load from user preference
}
LocaleConfig configures locale middleware.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory fixed-window rate limit store.
func NewMemoryStore ¶
func NewMemoryStore(opts ...MemoryStoreOption) *MemoryStore
NewMemoryStore returns a new in-memory rate limit store.
func (*MemoryStore) Allow ¶
func (s *MemoryStore) Allow(_ context.Context, key string, rate int, dur time.Duration) (bool, int, time.Time, error)
Allow implements RateLimitStore.
func (*MemoryStore) CleanupExpired ¶
func (s *MemoryStore) CleanupExpired(window time.Duration)
CleanupExpired removes expired windows.
type MemoryStoreOption ¶
type MemoryStoreOption func(*MemoryStore)
MemoryStoreOption configures a MemoryStore.
func WithAutoCleanup ¶
func WithAutoCleanup(ctx context.Context, interval time.Duration) MemoryStoreOption
WithAutoCleanup starts a background goroutine that calls CleanupExpired at the given interval. The goroutine stops when ctx is cancelled.
func WithMaxSize ¶
func WithMaxSize(n int) MemoryStoreOption
WithMaxSize sets the maximum number of entries in the memory store. When full, the oldest entry is evicted.
func WithWindow ¶
func WithWindow(d time.Duration) MemoryStoreOption
WithWindow sets the rate limit window duration used by autoCleanup when expiring entries. If unset, autoCleanup falls back to cleanupInterval.
type PGStore ¶
type PGStore struct {
// contains filtered or unexported fields
}
PGStore is a PostgreSQL-backed fixed-window rate limit store using an UNLOGGED table for performance.
func NewPGStore ¶
NewPGStore returns a new PostgreSQL rate limit store.
func (*PGStore) Allow ¶
func (s *PGStore) Allow(ctx context.Context, key string, rate int, dur time.Duration) (bool, int, time.Time, error)
Allow implements RateLimitStore using an atomic upsert.
type PageOverride ¶
PageOverride maps a specific status code to an ErrorPage.
func Page ¶
func Page(code int, page ErrorPage) PageOverride
Page creates a per-status-code override.
type RateLimitConfig ¶
type RateLimitConfig struct {
Store RateLimitStore
Rate int // max requests per window (default: 60)
Burst int // additional burst allowance over Rate (default: 10)
Window time.Duration // sliding window duration (default: 1 minute)
KeyFunc func(c echo.Context) (string, error) // default: c.RealIP()
FailClosed bool // deny requests when store errors (default: false)
}
RateLimitConfig configures rate limiting middleware.
type RateLimitStore ¶
type RateLimitStore interface {
Allow(ctx context.Context, key string, rate int, window time.Duration) (allowed bool, remaining int, resetAt time.Time, err error)
}
RateLimitStore checks whether a key is within its rate limit.
type RoleChecker ¶
RoleChecker returns true if the subject has at least one of the given roles.
type SecureConfig ¶
type SecureConfig struct {
ContentSecurityPolicy string // default: "default-src 'self'"
XFrameOptions string // default: "DENY"
ReferrerPolicy string // default: "strict-origin-when-cross-origin"
XSSProtection string // default: "0"
ContentTypeNosniff string // default: "nosniff"
}
SecureConfig allows overriding security response headers. Zero-value fields use sensible defaults.
type SubjectLoader ¶
SubjectLoader loads a subject by ID. Projects provide their own implementation (e.g. loading a User from the database).
type TrustedSubjectConfig ¶
type TrustedSubjectConfig struct {
// value in SecretHeader (constant-time compared) before X-Subject-ID is
// honoured.
SharedSecret string
// SecretHeader carries SharedSecret. Default: "X-Internal-Secret".
SecretHeader string
// TrustedProxies, when non-empty, requires the direct peer IP (c.RealIP())
// to fall within one of these CIDRs. Unparseable entries are dropped, so a
// list with no valid CIDR can never satisfy the gate (fails closed).
//
// NOTE: this is only as trustworthy as c.RealIP(). Configure the server's
// trusted-proxy IP extractor (server.WithTrustedProxies) so X-Forwarded-For
// can't be spoofed; the default extractor uses the direct peer, which is safe.
TrustedProxies []string
}
TrustedSubjectConfig gates which requests may establish the subject identity from the X-Subject-ID header. With neither field set the header is trusted unconditionally (the legacy behaviour) — only safe on a fully internal network where nothing untrusted can reach the mount.