diff --git a/pkg/membership/cors_test.go b/pkg/membership/cors_test.go index a1786300..c905f0ad 100644 --- a/pkg/membership/cors_test.go +++ b/pkg/membership/cors_test.go @@ -4,6 +4,7 @@ import ( "net/http" "net/http/httptest" "path/filepath" + "strings" "testing" "github.com/enmanuel/unibus/pkg/blobstore" @@ -57,6 +58,12 @@ func TestCORSPreflightAllowedOrigin(t *testing.T) { if got := resp.Header.Get("Access-Control-Allow-Methods"); got == "" { t.Fatalf("Allow-Methods missing on preflight") } + // The control-plane request-auth headers a browser signs every request with must + // be allow-listed, or the browser's preflight blocks the real request (the bug a + // live browser surfaced: listRooms failed with "Failed to fetch"). + if got := resp.Header.Get("Access-Control-Allow-Headers"); !strings.Contains(got, "X-Unibus-Sig") { + t.Fatalf("Allow-Headers must include the X-Unibus-* auth headers, got %q", got) + } } // TestCORSPreflightDisallowedOrigin: a preflight from an origin NOT in the allowlist diff --git a/pkg/membership/server.go b/pkg/membership/server.go index 08e82589..ee59bb59 100644 --- a/pkg/membership/server.go +++ b/pkg/membership/server.go @@ -258,7 +258,10 @@ func (s *Server) applyCORS(w http.ResponseWriter, r *http.Request) (preflight bo // origin. Add (not Set) to preserve any Vary the handler may add later. h.Add("Vary", "Origin") h.Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") - h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + // Allow the control-plane request-auth headers a browser client signs every + // request with (busauth.signedHeaders), or the browser's CORS preflight blocks + // the real request. Content-Type/Authorization stay for JSON bodies. + h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Unibus-Pub, X-Unibus-Ts, X-Unibus-Nonce, X-Unibus-Sig") h.Set("Access-Control-Max-Age", "600") } if r.Method == http.MethodOptions {