From 1b01fc6c4083e94a6b17877d23cdb077ac128ff3 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Fri, 11 Jul 2025 05:55:01 -0400 Subject: [PATCH] fix(api): CORS for connectRPC and grpc-web (#10227) # Which Problems Are Solved The CORS handler for the new connectRPC handlers was missing, leading to unhandled preflight requests and a unusable api for browser based calls, e.g. cross domain gRPC-web requests. # How the Problems Are Solved - Added the http CORS middleware to the connectRPC handlers. - Added `Grpc-Timeout`, `Connect-Protocol-Version`,`Connect-Timeout-Ms` to the default allowed headers (this improves also the old grpc-web handling) - Added `Grpc-Status`, `Grpc-Message`, `Grpc-Status-Details-Bin` to the default exposed headers (this improves also the old grpc-web handling) # Additional Changes None # Additional Context noticed internally while testing other issues --- internal/api/api.go | 2 +- internal/api/http/header.go | 54 ++++++++++--------- .../api/http/middleware/cors_interceptor.go | 6 +++ 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 349e9186bc..18c554ca37 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -201,7 +201,7 @@ func (a *API) registerConnectServer(service server.ConnectServer) { methodNames[i] = string(methods.Get(i).Name()) } a.connectServices[prefix] = methodNames - a.RegisterHandlerPrefixes(handler, prefix) + a.RegisterHandlerPrefixes(http_mw.CORSInterceptor(handler), prefix) } // HandleFunc allows registering a [http.HandlerFunc] on an exact diff --git a/internal/api/http/header.go b/internal/api/http/header.go index a6c2818728..fce5330df8 100644 --- a/internal/api/http/header.go +++ b/internal/api/http/header.go @@ -10,30 +10,36 @@ import ( ) const ( - Authorization = "authorization" - Accept = "accept" - AcceptLanguage = "accept-language" - CacheControl = "cache-control" - ContentType = "content-type" - ContentLength = "content-length" - ContentLocation = "content-location" - Expires = "expires" - Location = "location" - Origin = "origin" - Pragma = "pragma" - UserAgentHeader = "user-agent" - ForwardedFor = "x-forwarded-for" - ForwardedHost = "x-forwarded-host" - ForwardedProto = "x-forwarded-proto" - Forwarded = "forwarded" - ZitadelForwarded = "x-zitadel-forwarded" - XUserAgent = "x-user-agent" - XGrpcWeb = "x-grpc-web" - XRequestedWith = "x-requested-with" - XRobotsTag = "x-robots-tag" - IfNoneMatch = "If-None-Match" - LastModified = "Last-Modified" - Etag = "Etag" + Authorization = "authorization" + Accept = "accept" + AcceptLanguage = "accept-language" + CacheControl = "cache-control" + ContentType = "content-type" + ContentLength = "content-length" + ContentLocation = "content-location" + Expires = "expires" + Location = "location" + Origin = "origin" + Pragma = "pragma" + UserAgentHeader = "user-agent" + ForwardedFor = "x-forwarded-for" + ForwardedHost = "x-forwarded-host" + ForwardedProto = "x-forwarded-proto" + Forwarded = "forwarded" + ZitadelForwarded = "x-zitadel-forwarded" + XUserAgent = "x-user-agent" + XGrpcWeb = "x-grpc-web" + XRequestedWith = "x-requested-with" + XRobotsTag = "x-robots-tag" + IfNoneMatch = "if-none-match" + LastModified = "last-modified" + Etag = "etag" + GRPCTimeout = "grpc-timeout" + ConnectProtocolVersion = "connect-protocol-version" + ConnectTimeoutMS = "connect-timeout-ms" + GrpcStatus = "grpc-status" + GrpcMessage = "grpc-message" + GrpcStatusDetailsBin = "grpc-status-details-bin" ContentSecurityPolicy = "content-security-policy" XXSSProtection = "x-xss-protection" diff --git a/internal/api/http/middleware/cors_interceptor.go b/internal/api/http/middleware/cors_interceptor.go index 02f1924956..fb5503909d 100644 --- a/internal/api/http/middleware/cors_interceptor.go +++ b/internal/api/http/middleware/cors_interceptor.go @@ -21,6 +21,9 @@ var ( http_utils.XUserAgent, http_utils.XGrpcWeb, http_utils.XRequestedWith, + http_utils.ConnectProtocolVersion, + http_utils.ConnectTimeoutMS, + http_utils.GRPCTimeout, }, AllowedMethods: []string{ http.MethodOptions, @@ -34,6 +37,9 @@ var ( ExposedHeaders: []string{ http_utils.Location, http_utils.ContentLength, + http_utils.GrpcStatus, + http_utils.GrpcMessage, + http_utils.GrpcStatusDetailsBin, }, AllowOriginFunc: func(_ string) bool { return true