From 3271d8304a0fe8731c0fbb55957f9cef3457ef82 Mon Sep 17 00:00:00 2001 From: Jason Sievert Date: Fri, 21 Nov 2025 19:49:11 -0600 Subject: [PATCH 1/2] fix: Allow OPTIONS requests on /openapi.json for CORS preflight This change exempts OPTIONS requests from authentication on documentation endpoints (/docs, /redoc, /openapi.json) to support CORS preflight requests. Issue: Browser-based OpenAPI integrations (like Open WebUI) were failing because CORS preflight OPTIONS requests cannot include Authorization headers per RFC 7231 Section 4.3.7, but the DocsAuthMiddleware was enforcing authentication on all requests including OPTIONS. Solution: Check request.method == 'OPTIONS' before applying authentication, allowing CORS preflight to succeed while still requiring authentication for GET requests to actually fetch the OpenAPI spec. This maintains security (GET still requires auth) while enabling proper CORS support for browser-based integrations. Fixes browser-based OpenAPI tool integration (Open WebUI, Swagger UI, etc.) when AUTH_REQUIRED=false or when using proper authentication flows. --- mcpgateway/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mcpgateway/main.py b/mcpgateway/main.py index 6b30836e9..846a9704c 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -910,6 +910,10 @@ class DocsAuthMiddleware(BaseHTTPMiddleware): If a request to one of these paths is made without a valid token, the request is rejected with a 401 or 403 error. + Note: + OPTIONS requests are exempt from authentication to support CORS preflight + as per RFC 7231 Section 4.3.7 (OPTIONS must not require authentication). + Note: When DOCS_ALLOW_BASIC_AUTH is enabled, Basic Authentication is also accepted using BASIC_AUTH_USER and BASIC_AUTH_PASSWORD credentials. @@ -951,6 +955,11 @@ async def dispatch(self, request: Request, call_next): """ protected_paths = ["/docs", "/redoc", "/openapi.json"] + # Allow OPTIONS requests to pass through for CORS preflight (RFC 7231) + if request.method == "OPTIONS": + return await call_next(request) + + if any(request.url.path.startswith(p) for p in protected_paths): try: token = request.headers.get("Authorization") From ce822929bda12df571eb04c109137b648145bac5 Mon Sep 17 00:00:00 2001 From: Mihai Criveti Date: Thu, 27 Nov 2025 21:55:41 +0000 Subject: [PATCH 2/2] style: Remove extra blank line in DocsAuthMiddleware Fix minor style issue with double blank line after OPTIONS check. Signed-off-by: Mihai Criveti --- mcpgateway/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mcpgateway/main.py b/mcpgateway/main.py index 846a9704c..e494f1ca7 100644 --- a/mcpgateway/main.py +++ b/mcpgateway/main.py @@ -959,7 +959,6 @@ async def dispatch(self, request: Request, call_next): if request.method == "OPTIONS": return await call_next(request) - if any(request.url.path.startswith(p) for p in protected_paths): try: token = request.headers.get("Authorization")