Skip to content

Stop exposing manager API key in the browser bundle - #731

Merged
birme merged 1 commit into
mainfrom
frontend/fix-625-api-key-exposure
Oct 9, 2026
Merged

birme merged 1 commit into
mainfrom
frontend/fix-625-api-key-exposure

Conversation

@birme

@birme birme commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • src/api/api.ts read VITE_BACKEND_API_KEY via import.meta.env and sent it as an Authorization: Bearer header on every management request. All VITE_* vars are public by design (Vite inlines them into the browser bundle at build time), so any bundle built with the var set leaked the manager API key to anyone with DevTools (Security: VITE_BACKEND_API_KEY exposes API key in browser bundle #625, High severity).
  • Removed the API_KEY constant and all Authorization: Bearer headers. Requests now authenticate via the OSC platform session: the browser talks to the manager same-origin and the short-lived *.sat session cookie (kept fresh by the existing /reauth flow) is sent automatically. fetch defaults to credentials: "same-origin", so no extra client config is needed.
  • This is safe for production: the production Docker build never sets VITE_BACKEND_API_KEY (only MANAGER_URL/AUTH are baked in), so deployed bundles already send no Authorization header and rely on the session cookie today — this change only removes the dead-but-dangerous code path.
  • Updated the README: removed the VITE_BACKEND_API_KEY=<service-access-token> npm run dev instructions and added a security note explaining the frontend must never hold the key. (.env.local.sample never referenced the var, so no change was needed there.)

Test plan

  • npm test — 346 tests across 42 files pass
  • npm run typecheck — clean
  • npm run lint — clean (max-warnings 0)
  • Verified no VITE_BACKEND_API_KEY / API_KEY / Authorization references remain in src/
  • Confirmed no test asserted on the Authorization header (none did)
  • Rotate any previously-exposed manager API keys out-of-band (operational follow-up, not code)

Closes #625

🤖 Generated with Claude Code

VITE_* env vars are public by design — Vite inlines them into the browser
bundle at build time, so any static key read via import.meta.env is readable
from DevTools. api.ts read VITE_BACKEND_API_KEY and sent it as an
`Authorization: Bearer` header on every management request, exposing the
manager API key whenever the bundle was built with the var set.

Management endpoints are already authenticated by the OSC platform session:
the production Docker build never sets VITE_BACKEND_API_KEY, and requests are
authenticated same-origin by the short-lived `*.sat` cookie that the existing
/reauth flow keeps fresh. fetch defaults to credentials: "same-origin", so
that cookie rides along without any extra client config.

- Remove the API_KEY constant and every Authorization: Bearer header from
  api.ts; requests now rely on the same-origin session cookie.
- Drop the VITE_BACKEND_API_KEY dev instructions from the README and add a
  security note explaining why the frontend must not hold the key.

Closes #625

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@birme

birme commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Code Review — Verdict: LGTM

Automated self-review by today's daily-backlog-pr run (code-reviewer agent, a separate invocation from the implementer). Recorded as a marker comment because GitHub blocks state-bearing self-approval on a self-authored PR.

Summary: Clean, correct fix for the High-severity secret-exposure issue (#625). The manager API key is fully removed from the client — not merely relocated — and auth now relies on the OSC platform's same-origin session cookie, the mechanism the production deployment already used.

Blocking

None. Verified: zero reads of import.meta.env.VITE_BACKEND_API_KEY remain in src/ (only explanatory comment + README note); the removed Authorization header was already optional so same-origin production is unaffected; getBackendUrl() defaults to window.location.origin and fetch defaults to same-origin credentials so the *.sat cookie is attached; the 401 → reauth circuit-breaker path is intact; JSON POST/PATCH calls retain Content-Type.

Warnings (non-blocking)

  • Local-dev against a remote OSC instance (cross-origin, README's --disable-web-security flow) won't attach the cookie under default credentials: "same-origin" — a dev-workflow note or credentials: "include" guidance would help. Not a production bug.
  • No regression/guard test ships with the fix; a Vitest assertion that API calls carry no Authorization header (or a CI check forbidding VITE_BACKEND_API_KEY) would prevent silent reintroduction.

Suggestions

@birme
birme merged commit 8ce273f into main Oct 9, 2026
6 checks passed
@birme
birme deleted the frontend/fix-625-api-key-exposure branch October 9, 2026 17:57
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.

Security: VITE_BACKEND_API_KEY exposes API key in browser bundle

2 participants