package csrf import ( "context" "crypto/subtle" "log/slog" "net/http" ) const CsrfKey = "csrf" const CsrfLength = 32 func NewMiddleware(cookieName, formName string, secure bool) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(cookieName) var cookieValue string if err != nil || cookie.Value == "" { cookieValue, err = UpdateCsrf(w, cookieName, secure) if err != nil { slog.Error("update csrf", slog.String("op", "middleware.UpdateCsrf"), slog.Any("error", err)) http.Error(w, "Internal server error", http.StatusInternalServerError) return } } else { cookieValue = cookie.Value } ctx := context.WithValue(r.Context(), CsrfKey, cookieValue) r = r.WithContext(ctx) if r.Method == http.MethodGet || r.Method == http.MethodHead || r.Method == http.MethodOptions { next.ServeHTTP(w, r) return } formValue := r.FormValue(formName) if formValue == "" { formValue = r.Header.Get("X-CSRF-Token") } if subtle.ConstantTimeCompare([]byte(formValue), []byte(cookieValue)) != 1 { http.Error(w, "CSRF Token Validation Failed", http.StatusForbidden) return } next.ServeHTTP(w, r) }) } }