fix(membership): allow X-Unibus-* auth headers in CORS preflight
A browser signs every control-plane request with X-Unibus-Pub/Ts/Nonce/Sig (busauth.signedHeaders). The CORS Allow-Headers only listed Content-Type and Authorization, so the browser's preflight rejected the real request and the SPA failed with 'Failed to fetch' on the first authenticated call (listRooms). Add the four X-Unibus-* headers to Access-Control-Allow-Headers. This was invisible to the Node smoke (fetch in Node does no CORS preflight); only a real browser surfaced it. Verified live: enmanuel logs into uniweb against the cluster and lists rooms. Regression test asserts the header is present.
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/enmanuel/unibus/pkg/blobstore"
|
"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 == "" {
|
if got := resp.Header.Get("Access-Control-Allow-Methods"); got == "" {
|
||||||
t.Fatalf("Allow-Methods missing on preflight")
|
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
|
// TestCORSPreflightDisallowedOrigin: a preflight from an origin NOT in the allowlist
|
||||||
|
|||||||
@@ -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.
|
// origin. Add (not Set) to preserve any Vary the handler may add later.
|
||||||
h.Add("Vary", "Origin")
|
h.Add("Vary", "Origin")
|
||||||
h.Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
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")
|
h.Set("Access-Control-Max-Age", "600")
|
||||||
}
|
}
|
||||||
if r.Method == http.MethodOptions {
|
if r.Method == http.MethodOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user