Documentation
¶
Index ¶
- Constants
- Variables
- func APIKeyAuthMiddleware(store *APIKeyStore, next http.Handler) http.Handler
- func APIKeyNameFromContext(ctx context.Context) string
- func COOPHeaders(next http.Handler) http.Handler
- func CheckPassword(password, hash string) bool
- func ComponentLogger(name string) *slog.Logger
- func ContextWithTraceID(ctx context.Context, traceID string) context.Context
- func GenerateAPIKey() string
- func GenerateTraceID() string
- func HashPassword(password string) (string, error)
- func IsAPIKeyAuthenticated(ctx context.Context) bool
- func IsSessionToken(s string) bool
- func LoggerFromContext(ctx context.Context) *slog.Logger
- func NeedsRenewal(expiresAt, now time.Time) bool
- func NewAuthMiddleware(provider AuthProvider, plaintextPassword string, rateLimit AuthRateLimitConfig) (func(http.Handler) http.Handler, string)
- func RequestLogger(logger *slog.Logger, skipPaths ...string) func(next http.Handler) http.Handler
- func SecurityHeaders(frameAncestors string) func(http.Handler) http.Handler
- func SessionTokenFromRequest(r *http.Request) string
- func SetAuthMetrics(m *metrics.Metrics)
- func SetupLogger(level, format string) *slog.Logger
- func SignSessionToken(username, passwordBcryptHash string, now time.Time) (token string, expiresAt time.Time)
- func StreamingGzip(level int) func(http.Handler) http.Handler
- func TraceIDFromContext(ctx context.Context) string
- func TraceMiddleware(next http.Handler) http.Handler
- func VerifySessionToken(token, passwordBcryptHash string, now time.Time) (*tokenClaims, error)
- func WithAPIKeyName(ctx context.Context, name string) context.Context
- type APIKeyStore
- type AuthProvider
- type AuthRateLimitConfig
- type RateLimiter
- type RateLimiterConfig
- type StatusRecorder
Constants ¶
const APIKeyPrefix = "mbv_"
APIKeyPrefix identifies MiBeeVision API keys.
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).
const RenewedTokenHeader = "X-Renewed-Token"
RenewedTokenHeader is the response header carrying a freshly-signed token on sliding renewal.
const RequestIDHeader = "X-Request-Id"
RequestIDHeader is the HTTP response/request header carrying the trace ID.
const SessionTokenPrefix = "mbs_"
SessionTokenPrefix identifies browser session tokens (as opposed to "mbv_" API keys used by MiBeeVision).
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.
const TokenTTL = 2 * time.Hour
TokenTTL is how long a signed session token remains valid.
Variables ¶
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
APIKeyNameFromContext returns the authenticated API key name, or empty string.
func COOPHeaders ¶ added in v0.6.0
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 ¶
CheckPassword compares a plaintext password against a bcrypt hash. Results are cached for authCacheTTL to avoid repeated bcrypt overhead.
func ComponentLogger ¶
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
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 ¶
HashPassword generates a bcrypt hash from a plaintext password.
func IsAPIKeyAuthenticated ¶ added in v0.8.0
IsAPIKeyAuthenticated reports whether the request was authenticated via API Key.
func IsSessionToken ¶ added in v0.10.0
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
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
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 ¶
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 ¶
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
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
SetAuthMetrics injects the Prometheus metrics instance for auth tracking. Call once during startup (before serving requests).
func SetupLogger ¶
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
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
TraceIDFromContext extracts the trace ID from the context. Returns empty string if no trace ID is present.
func TraceMiddleware ¶ added in v0.8.0
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
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
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
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
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.
type RateLimiterConfig ¶ added in v0.3.0
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) WriteHeader ¶
func (r *StatusRecorder) WriteHeader(code int)