sulitha-agent / security.py
Sulitha's picture
initial deployment
7df55e6
"""
security.py
===========
Security layers:
1. CORS β€” portfolio domain only
2. X-Portfolio-Key β€” shared secret between Vercel proxy and this backend
(secret lives in Vercel server env, never in browser JS)
3. Rate limiting β€” slowapi, 10 req/min per IP
"""
import os
import logging
from fastapi import Request, HTTPException
from slowapi import Limiter
from slowapi.util import get_remote_address
logger = logging.getLogger(__name__)
# ── Secrets ───────────────────────────────────────────────────────────────────
PORTFOLIO_SECRET_KEY = os.environ.get("PORTFOLIO_SECRET_KEY", "")
# ── Allowed origins ───────────────────────────────────────────────────────────
# Add your Vercel domain here. localhost entries are for local dev only.
ALLOWED_ORIGINS = [
"https://sulitha-nulaksha-portfolio.vercel.app",
"http://localhost:3000",
"http://localhost:5173",
]
# ── Rate limiter ──────────────────────────────────────────────────────────────
limiter = Limiter(key_func=get_remote_address, default_limits=["10/minute"])
# ── Origin token check ────────────────────────────────────────────────────────
def verify_portfolio_key(request: Request) -> None:
"""
Reject requests that don't carry the correct X-Portfolio-Key header.
This header is set server-side by the Vercel API route proxy β€” it never
reaches the browser, so it cannot be scraped from frontend JS.
Skipped with a warning if PORTFOLIO_SECRET_KEY is not set (useful for
local dev without the full proxy setup).
"""
if not PORTFOLIO_SECRET_KEY:
logger.warning("PORTFOLIO_SECRET_KEY not set β€” skipping key check (dev mode).")
return
key = request.headers.get("X-Portfolio-Key", "")
if key != PORTFOLIO_SECRET_KEY:
raise HTTPException(status_code=403, detail="Forbidden")