From 7518fe643b7b54f4c81637637c50b6196e40ca4e Mon Sep 17 00:00:00 2001 From: chihlasm Date: Fri, 20 Mar 2026 06:05:20 +0000 Subject: [PATCH] fix(cors): return proper responses from middleware instead of re-raising Root cause: Both RequestLoggingMiddleware and ErrorLoggingMiddleware used BaseHTTPMiddleware and re-raised exceptions. When an exception (like a 401 from auth) was re-raised, the response never flowed back through CORSMiddleware, so browsers received error responses without CORS headers. This made 401 errors appear as CORS errors, breaking session resume and other operations after token expiry. Fix: Both middlewares now catch exceptions and return JSONResponse objects (with correct status codes from HTTPException) instead of re-raising. This ensures responses always flow through CORSMiddleware and receive proper Access-Control-Allow-Origin headers. Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/core/middleware.py | 38 ++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/app/core/middleware.py b/backend/app/core/middleware.py index be97e452..dcd88ca6 100644 --- a/backend/app/core/middleware.py +++ b/backend/app/core/middleware.py @@ -85,8 +85,21 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware): exc_info=True ) - # Re-raise exception to be handled by FastAPI - raise + # Return a proper response so it flows through CORSMiddleware. + # Re-raising from BaseHTTPMiddleware bypasses CORS headers. + from starlette.responses import JSONResponse + from fastapi import HTTPException + if isinstance(exc, HTTPException): + return JSONResponse( + status_code=exc.status_code, + content={"detail": exc.detail}, + headers={"X-Correlation-ID": correlation_id, "X-Process-Time": f"{process_time:.3f}"}, + ) + return JSONResponse( + status_code=500, + content={"detail": "Internal server error"}, + headers={"X-Correlation-ID": correlation_id, "X-Process-Time": f"{process_time:.3f}"}, + ) class ErrorLoggingMiddleware(BaseHTTPMiddleware): @@ -95,6 +108,11 @@ class ErrorLoggingMiddleware(BaseHTTPMiddleware): Ensures all exceptions are logged before being returned to the client, providing full stack traces for debugging. + + IMPORTANT: Returns a JSONResponse instead of re-raising so the response + flows back through CORSMiddleware and gets proper CORS headers. Re-raising + exceptions from BaseHTTPMiddleware bypasses CORS, causing browsers to + report CORS errors instead of the actual error (e.g., 401). """ async def dispatch( @@ -114,5 +132,17 @@ class ErrorLoggingMiddleware(BaseHTTPMiddleware): exc_info=True ) - # Re-raise to let FastAPI handle the response - raise + # Return a proper response so it flows through CORSMiddleware. + # If we re-raise, the response never passes through CORS and + # browsers see a CORS error instead of the actual error. + from starlette.responses import JSONResponse + from fastapi import HTTPException + if isinstance(exc, HTTPException): + return JSONResponse( + status_code=exc.status_code, + content={"detail": exc.detail}, + ) + return JSONResponse( + status_code=500, + content={"detail": "Internal server error"}, + )