1
0
forked from wrenn/wrenn
Files
wrenn-releases/internal/api/middleware_csrf.go
Rafeed M. Bhuiyan 05ddf62399 v0.2.0 (#50)
Co-authored-by: Tasnim Kabir Sadik <tksadik@omukk.dev>

Reviewed-on: wrenn/wrenn#50
2026-05-24 21:10:37 +00:00

39 lines
1.2 KiB
Go

package api
import (
"crypto/subtle"
"net/http"
"git.omukk.dev/wrenn/wrenn/pkg/auth"
)
// requireCSRF enforces double-submit CSRF: the wrenn_csrf cookie value must
// equal the X-CSRF-Token header. Skipped for safe methods (GET/HEAD/OPTIONS)
// and for requests authenticated via X-API-Key (SDK clients are not
// vulnerable to cross-site request forgery against cookie auth).
func requireCSRF() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet, http.MethodHead, http.MethodOptions:
next.ServeHTTP(w, r)
return
}
// API-key auth path: no CSRF check needed.
if ac, ok := auth.FromContext(r.Context()); ok && ac.APIKeyID.Valid {
next.ServeHTTP(w, r)
return
}
cookie, err := r.Cookie(csrfCookieName)
header := r.Header.Get("X-CSRF-Token")
if err != nil || cookie.Value == "" || header == "" ||
subtle.ConstantTimeCompare([]byte(cookie.Value), []byte(header)) != 1 {
writeError(w, http.StatusForbidden, "csrf_failed", "missing or invalid CSRF token")
return
}
next.ServeHTTP(w, r)
})
}
}