Spaces:
Running
Running
| """ | |
| API Key authentication middleware. | |
| Validates the HttpOnly session cookie (default) or X-Tenant-ID header (fallback | |
| for cross-origin file uploads that bypass the Vercel proxy). | |
| The session cookie is HMAC-signed and preferred. The X-Tenant-ID header is a | |
| weaker fallback — it accepts any valid tenant_id since these are already visible | |
| in the frontend — used only for direct file uploads to avoid Vercel's 4.5MB | |
| Serverless Function body limit. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from fastapi import HTTPException, Request | |
| from src.api.middleware.session import verify_session_token | |
| logger = logging.getLogger(__name__) | |
| _EXEMPT_PREFIXES = ("/api/v1/health", "/api/v1/session", "/health/") | |
| async def verify_api_key(request: Request) -> str: | |
| """ | |
| Verify the session cookie and attach tenant_id to request.state. | |
| Falls back to X-Tenant-ID header for cross-origin requests (e.g. direct | |
| file uploads bypassing the Vercel proxy). | |
| Exempts health, session/init, and root endpoints. | |
| """ | |
| path = request.url.path | |
| if any(path.startswith(p) for p in _EXEMPT_PREFIXES): | |
| return "" | |
| session_token = request.cookies.get("session") | |
| if session_token: | |
| tenant_id = verify_session_token(session_token) | |
| if tenant_id: | |
| request.state.tenant_id = tenant_id | |
| return session_token | |
| # Session cookie present but invalid (expired or tampered) | |
| raise HTTPException( | |
| status_code=401, | |
| detail={ | |
| "error": "invalid_session", | |
| "message": "Your session has expired or is invalid. Please start a new session.", | |
| "solution": "Call /api/v1/session/init to create a new session.", | |
| }, | |
| ) | |
| # Fallback: X-Tenant-ID header (weaker auth, for cross-origin uploads) | |
| tenant_id = request.headers.get("X-Tenant-ID") | |
| if tenant_id: | |
| from src.storage.tenant_store import get_tenant_store | |
| if get_tenant_store().tenant_exists(tenant_id): | |
| request.state.tenant_id = tenant_id | |
| return tenant_id | |
| raise HTTPException( | |
| status_code=401, | |
| detail={ | |
| "error": "invalid_tenant", | |
| "message": f"Tenant ID '{tenant_id}' does not exist.", | |
| "solution": "Provide a valid Tenant ID or call /api/v1/session/init to create a new session.", | |
| }, | |
| ) | |
| raise HTTPException( | |
| status_code=401, | |
| detail={ | |
| "error": "not_authenticated", | |
| "message": "Not authenticated. No session cookie or Tenant ID provided.", | |
| "solution": "Call /api/v1/session/init first to create a session.", | |
| }, | |
| ) | |