package setup import ( "crypto/rand" "encoding/hex" "net/http" ) const csrfCookieName = "felhom_csrf" const csrfFormField = "_csrf" // generateCSRFToken creates a random 32-byte hex token. func generateCSRFToken() string { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { // Fallback to time-based (extremely unlikely) return "fallback-csrf-token" } return hex.EncodeToString(b) } // setCSRFCookie sets the CSRF cookie on the response. func setCSRFCookie(w http.ResponseWriter, token string) { http.SetCookie(w, &http.Cookie{ Name: csrfCookieName, Value: token, Path: "/", SameSite: http.SameSiteStrictMode, HttpOnly: false, // JavaScript needs to read it for AJAX if needed }) } // validateCSRF checks that the form field matches the cookie. func validateCSRF(r *http.Request) bool { cookie, err := r.Cookie(csrfCookieName) if err != nil || cookie.Value == "" { return false } formToken := r.FormValue(csrfFormField) if formToken == "" { return false } return cookie.Value == formToken } // ensureCSRFToken returns the existing CSRF token from the cookie, or generates a new one. func ensureCSRFToken(w http.ResponseWriter, r *http.Request) string { if cookie, err := r.Cookie(csrfCookieName); err == nil && cookie.Value != "" { return cookie.Value } token := generateCSRFToken() setCSRFCookie(w, token) return token }