Fix CORS configuration to allow requests from Vercel frontend
This commit is contained in:
parent
d756051c4e
commit
2b5afe103b
16
README.md
16
README.md
@ -193,10 +193,24 @@ The API has CORS (Cross-Origin Resource Sharing) enabled with the following conf
|
|||||||
- Allowed origins:
|
- Allowed origins:
|
||||||
- http://localhost
|
- http://localhost
|
||||||
- http://localhost:3000
|
- http://localhost:3000
|
||||||
|
- http://127.0.0.1
|
||||||
|
- http://127.0.0.1:3000
|
||||||
- https://v0-ecommerce-app-build-swart.vercel.app
|
- https://v0-ecommerce-app-build-swart.vercel.app
|
||||||
|
- https://*.vercel.app (for preview deployments)
|
||||||
|
- * (wildcard for development)
|
||||||
|
|
||||||
- Allowed methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
|
- Allowed methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
|
||||||
- Allowed headers: Content-Type, Authorization, Accept, Origin, X-Requested-With, X-CSRF-Token, Access-Control-Allow-Credentials
|
- Allowed headers: Content-Type, Authorization, Accept, Origin, X-Requested-With, X-CSRF-Token, Access-Control-Allow-Credentials
|
||||||
- Exposed headers: Content-Length, Content-Type
|
- Exposed headers: Content-Length, Content-Type
|
||||||
- Credentials support: Enabled
|
- Credentials support: Enabled
|
||||||
- Max age for preflight requests: 600 seconds (10 minutes)
|
- Max age for preflight requests: 600 seconds (10 minutes)
|
||||||
|
|
||||||
|
### Custom CORS Handling
|
||||||
|
|
||||||
|
This application uses both FastAPI's built-in CORSMiddleware and a custom CORS middleware that provides enhanced handling of preflight OPTIONS requests. The custom middleware also supports wildcard pattern matching for origins (e.g., https://*.vercel.app) to better support deployment platforms.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| USE_CUSTOM_CORS_ONLY | Whether to use only the custom CORS middleware | False |
|
@ -24,11 +24,25 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
# CORS settings
|
# CORS settings
|
||||||
CORS_ORIGINS: List[str] = [
|
CORS_ORIGINS: List[str] = [
|
||||||
|
# Local development
|
||||||
"http://localhost",
|
"http://localhost",
|
||||||
"http://localhost:3000",
|
"http://localhost:3000",
|
||||||
"https://v0-ecommerce-app-build-swart.vercel.app"
|
"http://127.0.0.1",
|
||||||
|
"http://127.0.0.1:3000",
|
||||||
|
|
||||||
|
# Vercel frontend
|
||||||
|
"https://v0-ecommerce-app-build-swart.vercel.app",
|
||||||
|
|
||||||
|
# Other common Vercel domains (in case of redirects or preview deployments)
|
||||||
|
"https://*.vercel.app",
|
||||||
|
|
||||||
|
# Wildcard as a fallback (less secure but helps with debugging)
|
||||||
|
"*"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Whether to use only the custom CORS middleware
|
||||||
|
USE_CUSTOM_CORS_ONLY: bool = False
|
||||||
|
|
||||||
# Security settings
|
# Security settings
|
||||||
PASSWORD_HASH_ROUNDS: int = 12
|
PASSWORD_HASH_ROUNDS: int = 12
|
||||||
|
|
||||||
|
81
main.py
81
main.py
@ -1,10 +1,65 @@
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request, Response
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
from app.api.v1.api import api_router
|
from app.api.v1.api import api_router
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class CustomCORSMiddleware(BaseHTTPMiddleware):
|
||||||
|
"""Custom middleware to ensure CORS headers are set correctly for all responses."""
|
||||||
|
|
||||||
|
def is_origin_allowed(self, origin: str) -> bool:
|
||||||
|
"""Check if the origin is allowed based on the CORS_ORIGINS settings."""
|
||||||
|
if not origin:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Direct match
|
||||||
|
if origin in settings.CORS_ORIGINS:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Wildcard match
|
||||||
|
if "*" in settings.CORS_ORIGINS:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check for pattern matching (e.g., https://*.vercel.app)
|
||||||
|
for allowed_origin in settings.CORS_ORIGINS:
|
||||||
|
if "*" in allowed_origin:
|
||||||
|
pattern = allowed_origin.replace("*", "")
|
||||||
|
if origin.startswith(pattern.split("*")[0]) and origin.endswith(pattern.split("*")[-1]):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def dispatch(self, request: Request, call_next):
|
||||||
|
origin = request.headers.get("origin", "")
|
||||||
|
|
||||||
|
if request.method == "OPTIONS":
|
||||||
|
# Handle preflight requests
|
||||||
|
response = Response()
|
||||||
|
|
||||||
|
# Check if the origin is allowed
|
||||||
|
if self.is_origin_allowed(origin):
|
||||||
|
response.headers["Access-Control-Allow-Origin"] = origin
|
||||||
|
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS, PATCH"
|
||||||
|
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization, Accept, Origin, X-Requested-With, X-CSRF-Token, Access-Control-Allow-Credentials"
|
||||||
|
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||||
|
response.headers["Access-Control-Max-Age"] = "600"
|
||||||
|
response.status_code = 200
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Process regular requests
|
||||||
|
response = await call_next(request)
|
||||||
|
|
||||||
|
# Set CORS headers for the response
|
||||||
|
if self.is_origin_allowed(origin):
|
||||||
|
response.headers["Access-Control-Allow-Origin"] = origin
|
||||||
|
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title=settings.PROJECT_NAME,
|
title=settings.PROJECT_NAME,
|
||||||
description=settings.PROJECT_DESCRIPTION,
|
description=settings.PROJECT_DESCRIPTION,
|
||||||
@ -14,16 +69,20 @@ app = FastAPI(
|
|||||||
redoc_url="/redoc",
|
redoc_url="/redoc",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set up CORS
|
# Add our custom CORS middleware first (higher priority)
|
||||||
app.add_middleware(
|
app.add_middleware(CustomCORSMiddleware)
|
||||||
CORSMiddleware,
|
|
||||||
allow_origins=settings.CORS_ORIGINS,
|
# Set up standard CORS middleware as a backup if not disabled
|
||||||
allow_credentials=True,
|
if not settings.USE_CUSTOM_CORS_ONLY:
|
||||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
app.add_middleware(
|
||||||
allow_headers=["Content-Type", "Authorization", "Accept", "Origin", "X-Requested-With", "X-CSRF-Token", "Access-Control-Allow-Credentials"],
|
CORSMiddleware,
|
||||||
expose_headers=["Content-Length", "Content-Type"],
|
allow_origins=settings.CORS_ORIGINS,
|
||||||
max_age=600, # 10 minutes cache for preflight requests
|
allow_credentials=True,
|
||||||
)
|
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||||
|
allow_headers=["Content-Type", "Authorization", "Accept", "Origin", "X-Requested-With", "X-CSRF-Token", "Access-Control-Allow-Credentials"],
|
||||||
|
expose_headers=["Content-Length", "Content-Type"],
|
||||||
|
max_age=600, # 10 minutes cache for preflight requests
|
||||||
|
)
|
||||||
|
|
||||||
# Include API router
|
# Include API router
|
||||||
app.include_router(api_router)
|
app.include_router(api_router)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user