From 658997a308deeb5baa55bf8266b20070974abcfa Mon Sep 17 00:00:00 2001 From: jackthepunished <107313375+jackthepunished@users.noreply.github.com> Date: Mon, 25 May 2026 05:57:52 +0300 Subject: [PATCH] fix(cors): mirror Origin header when credentials are allowed (#679) Browsers reject `Access-Control-Allow-Origin: *` combined with `Access-Control-Allow-Credentials: true`. The CORS middleware now echoes the request Origin (with `Vary: Origin`) when present and only sends the credentials header in that case; falls back to `*` without credentials when there is no Origin. --- pkg/httputil/middleware.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/httputil/middleware.go b/pkg/httputil/middleware.go index 8bcf6be2c..bb1677525 100644 --- a/pkg/httputil/middleware.go +++ b/pkg/httputil/middleware.go @@ -72,13 +72,24 @@ func Logger(logger *slog.Logger) gin.HandlerFunc { } // CORS enables permissive CORS headers for development. +// +// Browsers reject responses that combine `Access-Control-Allow-Origin: *` +// with `Access-Control-Allow-Credentials: true`. When the request supplies +// an `Origin` header we mirror it back (with `Vary: Origin`) and allow +// credentials; otherwise we fall back to the wildcard with no credentials. func CORS() gin.HandlerFunc { return func(c *gin.Context) { - c.Header("Access-Control-Allow-Origin", "*") // For development, allow all. Could restrict to localhost:3000 + origin := c.GetHeader("Origin") + if origin != "" { + c.Header("Access-Control-Allow-Origin", origin) + c.Header("Vary", "Origin") + c.Header("Access-Control-Allow-Credentials", "true") + } else { + c.Header("Access-Control-Allow-Origin", "*") + } c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH") c.Header("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, x-api-key") c.Header("Access-Control-Expose-Headers", "Content-Length") - c.Header("Access-Control-Allow-Credentials", "true") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204)