fix: add CORS preflight handler and ACAO header for embed config endpoint

The GET /public/embed/config/{token} endpoint is fetched by external
websites (third-party embed sites). The global CORSMiddleware only covers
first-party origins, so external origins received no Access-Control-Allow-
Origin header, causing browser preflight failures.

Add an OPTIONS /config/{token} handler that validates the origin against the
token's allowed_domains list and returns the appropriate CORS headers.
Also inject Access-Control-Allow-Origin into the GET response via FastAPI's
response parameter so the actual request succeeds cross-origin.

Closes #383
This commit is contained in:
Varun Nuthalapati 2026-06-02 08:43:33 -07:00
parent acc2ef9e96
commit 230ed1846e
2 changed files with 118 additions and 1 deletions

View file

@ -0,0 +1,85 @@
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from api.routes.public_embed import router
app = FastAPI()
app.include_router(router, prefix="/api/v1")
client = TestClient(app, raise_server_exceptions=False)
_ACTIVE_TOKEN = SimpleNamespace(
is_active=True,
expires_at=None,
allowed_domains=[],
workflow_id=1,
settings={},
)
_RESTRICTED_TOKEN = SimpleNamespace(
is_active=True,
expires_at=None,
allowed_domains=["allowed.example.com"],
workflow_id=2,
settings={},
)
@pytest.fixture(autouse=True)
def _patch_db(monkeypatch):
async def _get_token(token):
if token == "valid":
return _ACTIVE_TOKEN
if token == "restricted":
return _RESTRICTED_TOKEN
return None
monkeypatch.setattr(
"api.routes.public_embed.db_client.get_embed_token_by_token",
_get_token,
)
def test_options_config_returns_acao_for_allowed_origin():
resp = client.options(
"/api/v1/public/embed/config/valid",
headers={"Origin": "https://mysite.vercel.app"},
)
assert resp.status_code == 200
assert resp.headers.get("access-control-allow-origin") == "https://mysite.vercel.app"
def test_options_config_rejects_unknown_token():
resp = client.options(
"/api/v1/public/embed/config/unknown",
headers={"Origin": "https://mysite.vercel.app"},
)
assert resp.status_code == 403
def test_options_config_rejects_disallowed_origin():
resp = client.options(
"/api/v1/public/embed/config/restricted",
headers={"Origin": "https://notallowed.example.com"},
)
assert resp.status_code == 403
def test_get_config_includes_acao_header():
resp = client.get(
"/api/v1/public/embed/config/valid",
headers={"Origin": "https://mysite.vercel.app"},
)
assert resp.status_code == 200
assert resp.headers.get("access-control-allow-origin") == "https://mysite.vercel.app"
def test_get_config_rejects_disallowed_origin():
resp = client.get(
"/api/v1/public/embed/config/restricted",
headers={"Origin": "https://notallowed.example.com"},
)
assert resp.status_code == 403