Skip to content

feat(server): BEE-1794 enable CORS for browser clients#6

Merged
alfredrc merged 1 commit into
developfrom
feat/bee-1794-cors
Apr 29, 2026
Merged

feat(server): BEE-1794 enable CORS for browser clients#6
alfredrc merged 1 commit into
developfrom
feat/bee-1794-cors

Conversation

@alfredrc
Copy link
Copy Markdown
Member

Summary

  • Drogon CORS module: pure config (CSV → whitelist) + advice (preRouting OPTIONS short-circuit, postHandling Allow-Origin stamping). 3 unit tests + 23 assertions.
  • Server reads BEEPBOX_CORS_ALLOWED_ORIGINS env var; empty = CORS off (server-to-server flows unchanged).
  • Terraform variable + Cloud Run env wired.

Closes BEE-1794. Unblocks BEE-1686 (Commlink track encode step blocked on missing CORS preflight headers).

Test plan

  • Local build clean (server + tests)
  • Unit tests pass (`build/BeepBoxTests "[cors]"` → 23/23 assertions; full suite 606/606)
  • Smoke-tested locally: `curl -X OPTIONS -H "Origin: http://localhost:3000"\` → 204 + 4 `Access-Control-*` headers. Disallowed origin → 204 with no CORS headers. Server-to-server (no Origin) flow unchanged.
  • Deployed to Cloud Run dev via `gh workflow run deploy.yml --ref develop -f image_tag=sha-` (post-merge)
  • Verified dev: `curl -X OPTIONS -H "Origin: http://localhost:3000" https://beepbox-server-ai7n45q5lq-ew.a.run.app/v1/encode\` returns CORS headers
  • BEE-1686 QA encode step click → 200 OK in browser

🤖 Generated with Claude Code

Drogon doesn't ship CORS middleware. The beepbox-server image deployed
to Cloud Run was rejecting every browser preflight (OPTIONS without
Access-Control-Allow-Origin) → BEE-1686 Commlink track encode step
broken end-to-end:

  Access to fetch at '.../v1/encode' from origin 'http://localhost:3000'
  has been blocked by CORS policy: Response to preflight request doesn't
  pass access control check: No 'Access-Control-Allow-Origin' header is
  present on the requested resource.

This commit adds a self-contained CORS module:

  include/beepbox/CorsConfig.h
  src/CorsConfig.cpp        # Pure parsing + origin resolution (no Drogon)
  src/CorsAdvice.cpp        # Drogon preRoutingAdvice + postHandlingAdvice

Split exists so unit tests link only the pure logic file (no Drogon
runtime needed) — keeps the test suite fast.

Behaviour:

  - Whitelist comes from BEEPBOX_CORS_ALLOWED_ORIGINS env var (CSV).
    Empty / unset → CORS disabled, request pipeline behaves identically
    to pre-1794 server-to-server-only mode.
  - PreRoutingAdvice intercepts OPTIONS *before* the auth filter, so
    the browser preflight (no Bearer) gets a 204 with the four
    Access-Control-* headers without tripping the API key gate.
  - PostHandlingAdvice stamps Access-Control-Allow-Origin + Vary: Origin
    on every non-OPTIONS response when the request `Origin` matches the
    whitelist. No-op for callers without an `Origin` header → server-to-
    server flows unchanged.
  - Exact-match origins only — no wildcards, no scheme coercion. RFC
    6454 verbatim comparison (case-sensitive on host).

Server startup logs the active whitelist (or "CORS disabled") for
observability.

Infra:

  infra/cloud-run.tf            # Pipes the env var into Cloud Run
  infra/variables.tf            # New `cors_allowed_origins` variable
  infra/terraform.tfvars.example # Documents dev defaults

Tests: 3 new test cases / 23 new assertions (parseAllowedOrigins · CSV
trimming + order preservation · whitelist resolution including verbatim
mismatch on scheme/port/case · enabled() flag). Full suite:
85 / 85 (was 82 → +3). beepbox-server build clean, smoke-tested
locally with `curl -X OPTIONS -H "Origin: http://localhost:3000"
http://localhost:8080/v1/encode` → 204 + all four headers; same with
`Origin: https://evil.com` → 204 with no CORS headers (browser blocks).

Closes BEE-1794. Unblocks BEE-1686.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alfredrc alfredrc merged commit 96cf4a8 into develop Apr 29, 2026
3 checks passed
@alfredrc alfredrc deleted the feat/bee-1794-cors branch April 29, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant