package handler import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "time" ) // csrfToken returns an HMAC token valid for the current and previous hour. // Reuses the session secret so no additional secret is required. func csrfToken() string { return csrfTokenForTime(time.Now().UTC()) } func csrfTokenForTime(t time.Time) string { bucket := t.Truncate(time.Hour).Unix() mac := hmac.New(sha256.New, sessionSecret()) mac.Write([]byte(fmt.Sprintf("csrf:%d", bucket))) return hex.EncodeToString(mac.Sum(nil)) } // csrfValid returns true if token matches the current or previous hour's token. func csrfValid(token string) bool { if token == "" { return false } now := time.Now().UTC() for _, t := range []time.Time{now, now.Add(-time.Hour)} { expected := csrfTokenForTime(t) if hmac.Equal([]byte(token), []byte(expected)) { return true } } return false }