// Package csrf // Path: /internal/platform/csrf // File: csrf.go // // Purpose // // Centralized CSRF protection wrapper using justinas/nosurf. // Applies default CSRF protections across the entire HTTP handler tree // after SCS session load/save wrapping. // // Responsibilities (as implemented here) // 1. Construct a nosurf middleware handler over the provided http.Handler. // 2. Configure the base CSRF cookie using values from the App configuration. // 3. Enforce HttpOnly and SameSite=Lax defaults. // 4. Enable Secure flag automatically in production mode. // // HTTP stack order (per bootstrap) // // Gin Router → SCS LoadAndSave → CSRF Wrapper → http.Server // // Design notes // - The nosurf package automatically: // - Inserts CSRF token into responses (e.g., via nosurf.Token(c.Request)) // - Validates token on state-changing requests (POST, PUT, etc.) // - CSRF cookie name is configurable via config.Config. // - Secure flag is tied to cfg.HttpServer.ProductionMode (recommended). // - Global protection: all routed POSTs are covered automatically. // // TODOs (observations from current implementation) // - Expose helper to fetch token into Gin templates via context key. // - Consider SameSiteStrictMode once OAuth/external logins are defined. // - Add domain and MaxAge settings for more precise control. // - Provide per-route opt-outs if needed for webhook endpoints. // // Change log // // [2025-10-29] Documentation updated to reflect middleware position and cookie policy. package csrf import ( "net/http" "synlotto-website/internal/platform/config" "github.com/justinas/nosurf" ) // Wrap applies nosurf CSRF middleware to the given handler, // configuring the CSRF cookie based on App configuration. // // Caller must ensure this is positioned *outside* SCS LoadAndSave // so CSRF can access session data when generating/validating tokens. func Wrap(h http.Handler, cfg config.Config) http.Handler { cs := nosurf.New(h) cs.SetBaseCookie(http.Cookie{ Name: cfg.CSRF.CookieName, Path: "/", HttpOnly: true, Secure: cfg.HttpServer.ProductionMode, SameSite: http.SameSiteLaxMode, }) return cs }