middleware

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const APIKeyPrefix = "mbv_"

APIKeyPrefix identifies MiBeeVision API keys.

View Source
const RenewThreshold = 15 * time.Minute

RenewThreshold is the remaining-lifetime below which the middleware issues a fresh token in the X-Renewed-Token response header (sliding renewal).

View Source
const RenewedTokenHeader = "X-Renewed-Token"

RenewedTokenHeader is the response header carrying a freshly-signed token on sliding renewal.

View Source
const RequestIDHeader = "X-Request-Id"

RequestIDHeader is the HTTP response/request header carrying the trace ID.

View Source
const SessionTokenPrefix = "mbs_"

SessionTokenPrefix identifies browser session tokens (as opposed to "mbv_" API keys used by MiBeeVision).

View Source
const StreamCookieName = "mbs_session"

StreamCookieName is the cookie that carries a session token for media players that cannot attach headers to every request (notably iOS AVPlayer, whose custom header support does not reliably apply to HLS segment requests). Issued by the HLS playlist handler; see #331.

View Source
const TokenTTL = 2 * time.Hour

TokenTTL is how long a signed session token remains valid.

Variables

View Source
var ErrInvalidToken = errors.New("invalid session token")

ErrInvalidToken is returned by Verify for any malformed, tampered, or expired token. Callers should treat all variants identically (reject).

Functions

func APIKeyAuthMiddleware added in v0.8.0

func APIKeyAuthMiddleware(store *APIKeyStore, next http.Handler) http.Handler

APIKeyAuthMiddleware validates Bearer tokens with the "mbv_" prefix against the live API key store. It runs alongside BasicAuth — if the request has a Bearer token, API Key auth is attempted first; otherwise BasicAuth handles it. This allows MiBeeVision (and per-device app tokens) to use API Keys while regular users continue using BasicAuth.

func APIKeyNameFromContext added in v0.8.0

func APIKeyNameFromContext(ctx context.Context) string

APIKeyNameFromContext returns the authenticated API key name, or empty string.

func COOPHeaders added in v0.6.0

func COOPHeaders(next http.Handler) http.Handler

COOPHeaders returns a middleware that adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers to responses when the connection uses TLS. These headers require a secure context (HTTPS) — on plain HTTP they are silently ignored by browsers and only produce console warnings.

func CheckPassword

func CheckPassword(password, hash string) bool

CheckPassword compares a plaintext password against a bcrypt hash. Results are cached for authCacheTTL to avoid repeated bcrypt overhead.

func ComponentLogger

func ComponentLogger(name string) *slog.Logger

ComponentLogger creates a logger with a component attribute. Returns a logger that includes the component name in all log messages.

func ContextWithTraceID added in v0.8.0

func ContextWithTraceID(ctx context.Context, traceID string) context.Context

ContextWithTraceID returns a new context with the trace ID embedded.

func GenerateAPIKey added in v0.8.0

func GenerateAPIKey() string

GenerateAPIKey creates a new random API key with the mbv_ prefix. Returns a 40-char hex string prefixed with "mbv_" (44 chars total).

func GenerateTraceID added in v0.8.0

func GenerateTraceID() string

GenerateTraceID returns a short random hex string suitable for log correlation. Uses crypto/rand for unpredictability (16 bytes → 32 hex chars, truncated to 16).

func HashPassword

func HashPassword(password string) (string, error)

HashPassword generates a bcrypt hash from a plaintext password.

func IsAPIKeyAuthenticated added in v0.8.0

func IsAPIKeyAuthenticated(ctx context.Context) bool

IsAPIKeyAuthenticated reports whether the request was authenticated via API Key.

func IsSessionToken added in v0.10.0

func IsSessionToken(s string) bool

IsSessionToken reports whether s looks like a session token (mbs_ prefix). Used by the auth middleware to route ?token= query params to the right path without trying to base64-decode them.

func LoggerFromContext added in v0.8.0

func LoggerFromContext(ctx context.Context) *slog.Logger

LoggerFromContext returns a slog.Logger pre-loaded with the trace_id attribute from the context. If no trace ID is present, the logger is returned without it. Use this in handlers to ensure every log line is correlated to its request:

logger := middleware.LoggerFromContext(r.Context())
logger.Info("processing recording", "id", recID)

func NeedsRenewal added in v0.10.0

func NeedsRenewal(expiresAt, now time.Time) bool

NeedsRenewal reports whether a token with the given expiry should be renewed at the current time (i.e. its remaining life is under RenewThreshold).

func NewAuthMiddleware

func NewAuthMiddleware(provider AuthProvider, plaintextPassword string, rateLimit AuthRateLimitConfig) (func(http.Handler) http.Handler, string)

NewAuthMiddleware returns a middleware that protects endpoints with HTTP Basic auth. If passwordHash is empty but plaintextPassword is non-empty, it is auto-hashed via bcrypt. Returns the middleware and the effective hash used (for config persistence). If both are empty, all requests return 503 Service Unavailable with setup guidance. The provider is called on every request so changes (e.g. setup) take effect immediately. rateLimit controls auth failure rate limiting; when .Enabled is false, no limiting is applied.

func RequestLogger

func RequestLogger(logger *slog.Logger, skipPaths ...string) func(next http.Handler) http.Handler

RequestLogger returns a middleware that logs each request using slog.LogAttrs. Paths in skipPaths are not logged. Each request gets a trace_id that is injected into the context and the X-Request-Id response header, so downstream handlers can correlate their logs via LoggerFromContext(ctx).

func SecurityHeaders

func SecurityHeaders(frameAncestors string) func(http.Handler) http.Handler

SecurityHeaders returns a middleware that adds common security headers to every response. HSTS is intentionally omitted: on LAN HTTP deployments it bricks access for a year. If TLS is needed, use a reverse proxy (Caddy/nginx) and let it set HSTS.

frameAncestors controls who may embed the UI in an <iframe> via the CSP frame-ancestors directive. It accepts a space-separated list of sources (e.g. "'self'", "http://192.168.1.10 http://192.168.1.11"). An empty value falls back to 'self' (no cross-origin framing). This replaces the legacy X-Frame-Options header, which cannot express a cross-origin allow-list and thus broke embedding in the fnOS desktop (the desktop page is served from a different origin than the NVR's :9090, so even SAMEORIGIN rejected it).

func SessionTokenFromRequest added in v0.11.0

func SessionTokenFromRequest(r *http.Request) string

SessionTokenFromRequest returns the session token (mbs_...) authenticating the current request, in header, query, or stream-cookie form. Exposed so media handlers can re-issue the stream cookie on playlist fetches.

func SetAuthMetrics added in v0.8.0

func SetAuthMetrics(m *metrics.Metrics)

SetAuthMetrics injects the Prometheus metrics instance for auth tracking. Call once during startup (before serving requests).

func SetupLogger

func SetupLogger(level, format string) *slog.Logger

SetupLogger creates and configures a logger with the specified level and format. Returns a configured slog.Logger instance.

func SignSessionToken added in v0.10.0

func SignSessionToken(username, passwordBcryptHash string, now time.Time) (token string, expiresAt time.Time)

SignSessionToken mints a new session token for the given username. passwordBcryptHash is the user's current bcrypt hash (the very same value the auth middleware already reads on every request via AuthProvider.GetHash); it is folded into the signing key so changing the password invalidates old tokens without any revocation list.

The token expires at TokenTTL from now. The returned expiry is the absolute time for the caller to hand back to clients (e.g. login response body).

func StreamingGzip added in v0.10.0

func StreamingGzip(level int) func(http.Handler) http.Handler

StreamingGzip is middleware that compresses responses with gzip. Unlike chi's built-in middleware.Compress, this implementation:

  • Flushes the gzip writer on every Flush() call, making it safe for SSE (text/event-stream) without buffering delays.
  • Skips already-compressed content types (video, images, archives).

The level parameter controls compression: 1 (BestSpeed) to 9 (BestCompression). Level 5 is a good default (close to BestSpeed with better ratio).

func TraceIDFromContext added in v0.8.0

func TraceIDFromContext(ctx context.Context) string

TraceIDFromContext extracts the trace ID from the context. Returns empty string if no trace ID is present.

func TraceMiddleware added in v0.8.0

func TraceMiddleware(next http.Handler) http.Handler

TraceMiddleware injects a trace ID into the request context and response header. If the incoming request already has an X-Request-Id header, it is reused (allows upstream proxies or clients to propagate correlation IDs).

func VerifySessionToken added in v0.10.0

func VerifySessionToken(token, passwordBcryptHash string, now time.Time) (*tokenClaims, error)

VerifySessionToken validates a token's signature and expiry and returns the parsed claims. Any failure (bad format, tampered payload, wrong key, expired) returns ErrInvalidToken. now lets tests inject time.

func WithAPIKeyName added in v0.10.0

func WithAPIKeyName(ctx context.Context, name string) context.Context

WithAPIKeyName returns a derived context carrying the API-key name, mirroring what APIKeyAuthMiddleware sets. Exported so tests (and only tests) can exercise handlers that gate on IsAPIKeyAuthenticated without standing up the full key map + middleware chain. Production code should authenticate via the middleware.

Types

type APIKeyStore added in v0.11.0

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

APIKeyStore is the live set of valid API keys plus per-key last-used timestamps. It replaces the static map snapshot previously captured at router-build time, so minting and revoking keys take effect on the next request without a service restart (#335).

func NewAPIKeyStore added in v0.11.0

func NewAPIKeyStore() *APIKeyStore

NewAPIKeyStore returns an empty store.

func (*APIKeyStore) LastUsed added in v0.11.0

func (s *APIKeyStore) LastUsed() map[string]time.Time

LastUsed returns a copy of the key-name → last-used timestamps.

func (*APIKeyStore) Lookup added in v0.11.0

func (s *APIKeyStore) Lookup(token string) (string, bool)

Lookup resolves a token to its key name. Usage timestamps are recorded with a per-key throttle; comparisons stay constant-time like the previous static-map implementation.

func (*APIKeyStore) SetKeys added in v0.11.0

func (s *APIKeyStore) SetKeys(keys map[string]string)

SetKeys atomically replaces the valid key set (token → name).

type AuthProvider added in v0.4.0

type AuthProvider struct {
	GetUsername func() string
	GetHash     func() string
}

AuthProvider returns the current username and effective password hash. Used by the auth middleware to dynamically read credentials (e.g. after setup).

type AuthRateLimitConfig added in v0.7.0

type AuthRateLimitConfig struct {
	Enabled       bool
	MaxFailures   int
	WindowMinutes int
}

AuthRateLimitConfig controls auth failure rate limiting.

type RateLimiter added in v0.8.0

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

RateLimiter provides per-IP rate limiting with automatic stale entry cleanup.

func NewRateLimiter added in v0.3.0

func NewRateLimiter(ctx context.Context, cfg RateLimiterConfig) *RateLimiter

NewRateLimiter creates a new RateLimiter and starts a background cleanup goroutine. The cleanup goroutine exits when ctx is cancelled. Every 2×Window period, stale entries (older than Window) are evicted.

func (*RateLimiter) Handler added in v0.8.0

func (rl *RateLimiter) Handler(next http.Handler) http.Handler

Handler returns an HTTP middleware that rate limits per client IP.

type RateLimiterConfig added in v0.3.0

type RateLimiterConfig struct {
	MaxRequests int
	Window      time.Duration
}

RateLimiterConfig defines parameters for a per-IP rate limiter.

type StatusRecorder

type StatusRecorder struct {
	http.ResponseWriter
	Status int
	Bytes  int
}

StatusRecorder wraps http.ResponseWriter to capture status code and response size.

func (*StatusRecorder) Flush added in v0.6.0

func (r *StatusRecorder) Flush()

Flush implements the http.Flusher interface. Required for SSE (Server-Sent Events) streaming endpoints.

func (*StatusRecorder) Hijack added in v0.6.0

func (r *StatusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error)

Hijack implements the http.Hijacker interface. Required for WebSocket upgrade — gorilla/websocket calls Hijack to take over the underlying TCP connection.

func (*StatusRecorder) Write

func (r *StatusRecorder) Write(b []byte) (int, error)

func (*StatusRecorder) WriteHeader

func (r *StatusRecorder) WriteHeader(code int)

Directories

Path Synopsis
Package remotelog implements an slog.Handler that batches and ships logs to VictoriaLogs (or any JSON-lines ingestion endpoint).
Package remotelog implements an slog.Handler that batches and ships logs to VictoriaLogs (or any JSON-lines ingestion endpoint).

Jump to

Keyboard shortcuts

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