Restoring Browser Extension Backend Calls: A Playbook
Diagnose and fix extension auth and generation failures by checking responses, cookies, CORS, team key isolation, and provider errors.
When a browser extension can't authenticate or generate against the backend
A browser extension that proxies model calls through the platform backend has stopped working on cross-site calls: it can't authenticate, or it can't generate. Because the extension is a thin client and every script calls the backend rather than the model provider directly [1825], the fault is almost always in the cross-origin contract — status codes, cookies, CORS, or the per-team key — not in the extension UI. This playbook reads the network call first, branches on what the response actually shows, then applies the least invasive fix. Do not touch extension code before you have inspected the call.
When this applies
Use this playbook when a browser extension that routes model calls through the platform backend fails on a cross-site request, specifically when:
Authentication failure
The extension can't authenticate even though the user is logged in to the platform in the same browser [1823].
A generate request fails, returns nothing, or crashes the flow [1826].
The popup status indicator freezes or logout does not take effect [1826].
A team appears to see another team's key, model, or output, or key isolation is in doubt [1827].
Generation failure
The architecture baseline this assumes: the extension is a thin client that calls the backend, auth reuses the existing login cookie, and the backend proxies each call with the team's own key and model [1823]. The backend surface is three endpoints — generate, session check, and logout — with CORS locked to the extension id and cookies tuned for cross-site requests [1824].
Severity & impact
Classify by blast radius first, then by whether tenant isolation is at risk.
| Severity | Blast radius | Example |
|---|---|---|
| SEV-1 | Cross-tenant: one team can reach another team's key, model, or output | Per-team key isolation not holding across teams [1827] |
| SEV-2 | All extension users of a build: no one can authenticate or generate | Redirect returned instead of 401 [1826], CORS not scoped to the extension id [1824] |
| SEV-3 | Degraded single flow: generation crashes, logout wrong, status frozen | Provider error crashes instead of failing gracefully, wrong logout method, frozen popup status [1826] |
Any suspicion that one team's request is served with another team's key or model is a SEV-1 tenant-isolation incident. A shared proxy holds every team's provider keys server-side [1823]; the core risk of that design is that a scoping mistake leaks one tenant into another. Treat isolation doubt as SEV-1 until two-team testing proves otherwise [1827].
Roles
Assign before touching anything; keep names generic and fill from rotation.
- Incident owner (on-call): runs the triage tree, decides mitigations, owns the timeline.
- Comms lead: posts updates, fields user-facing questions, shields the owner.
- Backend owner: owns the extension controller, its generate / session-check / logout endpoints, CORS, and cookie configuration [1824].
- Extension owner: owns the background, popup, and content scripts that call the backend [1825].
For a SEV-1 isolation incident, split comms out immediately and page the backend owner.
Triage steps
Everything here is read-only. Inspect the network call before changing any code. Do not restart, re-deploy, or edit scripts until the decision tree points you there.
- Capture the failing request and its response. Read the raw response to the extension's call to the backend — status code, headers, and body — do not rely on the popup status, which can freeze independently [1826].
- Is the auth failure returned as a clean 401, or as a redirect? An unauthenticated call that returns a redirect instead of a 401 means the client cannot detect the auth failure at all [1826]. A redirect on the generate or session-check endpoint is the signal here.
- Is the session cookie sent on the cross-site request? Check that the browser attached the login cookie to the cross-origin call. Auth reuses the existing login cookie [1823], and the cookies must be configured for cross-site requests for the browser to send them cross-origin [1824]. No cookie on the wire means no auth.
- Is CORS scoped to the extension id? Confirm the response CORS headers allow the extension id specifically, not a wildcard and not the wrong origin [1824]. A cross-origin call from the extension that CORS rejects will fail before auth is even evaluated.
- Is the per-team key valid, and is it the right team's key? Confirm the backend resolved the caller's team and proxied with that team's own key and model [1823]. Read which key/model the proxy selected for the request; it must match the caller's team, never another's [1827].
- Does a provider error surface gracefully? If the request reached the provider and the provider returned an error, confirm the flow degrades gracefully rather than crashing [1826].
Read-only diagnostic checklist:
[ ] Raw backend response captured (status, headers, body) for the failing call
[ ] Auth failure returns 401, not a redirect
[ ] Session (login) cookie present on the cross-site request
[ ] CORS response scoped to the extension id, not wildcard/other origin
[ ] Proxy resolved the caller's team and used that team's own key + model
[ ] No cross-team leak: key/model belongs to the caller's team only
[ ] Provider error path degrades gracefully (no crash)
[ ] Popup status treated as unreliable until the wire is read
Decision points
Branch on what the read-only pass showed.
- If an unauthenticated call returns a redirect instead of a 401, it is an auth-signalling failure: the client can't react because it never sees a 401 [1826]. → Auth branch.
- If the login cookie is absent from the cross-site request, cookies are not tuned for cross-site and the browser withheld the session [1824][1823]. → Auth branch.
- If CORS is not scoped to the extension id (wildcard, wrong origin, or missing), the cross-origin call is rejected before auth [1824]. → Auth branch.
- If auth is fine but generation fails, check the team key: a dead, missing, or wrong-team key means the proxy can't generate for that caller [1823][1827]. → Generation branch.
- If the request reached the provider and the provider errored, and the flow crashed rather than degrading, it is unhandled provider error [1826]. → Generation branch.
- If auth and generation both succeed on the wire but the popup status is frozen or logout did nothing, it is a UI/logout defect, not a backend fault [1826]. → UI branch.
- If any team's request resolves to another team's key or model, stop: this is a tenant-isolation breach, SEV-1 [1827]. → Escalate.
Mitigation menu
Apply the least invasive fix that resolves the confirmed root cause. Move up the list only when the cheaper fix does not fit the evidence.
- UI branch — fix the popup status so it reflects real state, and point logout at the correct method [1826]. No backend change needed.
- Generation branch, provider error — make the provider-error path fail gracefully instead of crashing the flow [1826].
- Auth branch, redirect-not-401 — return a real 401 status on unauthenticated calls so the client can detect and react to the auth failure, rather than a redirect [1826].
- Generation branch, team key — restore or correct the caller's own key and model on the backend so the proxy can generate for that team [1823]. Confirm the key resolves to the caller's team, never a shared or default one [1827].
- Auth branch, cookie — tune the cookie settings for cross-site requests so the browser sends the session on cross-origin calls [1824].
- Auth branch, CORS — lock CORS to the extension id so the cross-origin call is accepted from the extension and no other origin [1824].
- Tenant isolation — if any cross-team leak is confirmed, correct the per-team key/model resolution so each request uses only the caller's team's key, and re-verify isolation across two teams before shipping [1827][1823].
Two rules that override convenience: keys stay per-team and server-side — never widen scope to a shared or wildcard key to "unblock" generation, because a shared proxy's core risk is exactly a cross-tenant leak [1823][1827]. And auth failures must return real status codes, not redirects, so the client can react [1826].
Escalate when
Escalate the moment a cheaper fix does not fit the evidence.
- Escalate to the backend owner for any SEV-1 tenant-isolation breach — one team resolving to another team's key or model [1827][1823]. This blocks release.
- Escalate to the backend owner when the fix is in CORS scoping, cookie cross-site configuration, or the 401-vs-redirect status contract, since these live in the extension controller and its endpoints [1824][1826].
- Escalate to the extension owner when the fault is confined to the background, popup, or content scripts — frozen status or wrong logout method [1826][1825].
Rollback criteria: if a mitigation widens origin/CORS scope, exposes a shared key, or makes auth failures ambiguous again, revert it and drop back to the last read-only-confirmed state before trying the next item.
Verification & recovery
Verify before merge, from the client's side of the cross-site call, not just the backend log.
- Log in through the extension and confirm authentication succeeds on the cross-site call [1827].
- Force an unauthenticated call and confirm it returns a clean 401 that the client detects, not a redirect [1826].
- Run suggestion generation and refine end-to-end against the running backend and confirm they succeed [1827].
- Verify per-team key isolation across two teams: each team's request must use only its own key and model, with no cross-team leak [1827][1823].
- Verify session expiry and logout: logout must hit the correct method and take effect, and the popup status must update rather than freeze [1826][1827].
All of these must pass, with isolation holding across two teams, before the change merges [1827].
Evidence & comms
Capture evidence while the failing call is reproducible, before a redeploy overwrites it.
- The raw failing response: status code (401 vs redirect), CORS headers, and whether the cookie was on the wire [1826][1824].
- Which team, key, and model the proxy resolved for the request — proof of isolation or of a leak [1827][1823].
- The provider-error trace if generation crashed instead of degrading [1826].
- The two-team isolation test result and the session-expiry / logout result from pre-merge testing [1827].
Status-update template:
[SEV-x] Extension proxy — <UPDATE>
Impact: <auth / generation / UI; single flow, all users, or cross-tenant>
Root cause: <confirmed/suspected — redirect-not-401, cookie not sent, CORS scope, dead/wrong-team key, provider-error crash, UI/logout>
Isolation: <two-team check pass/fail>
Action: <read-only diagnostics done / mitigation applied>
Next update: <time>
Post-incident
Book a short retro and turn the root cause into a standing check.
- Keep auth failures returning real status codes: a redirect that hides a 401 leaves the client unable to react, so guard the 401 contract on every extension endpoint [1826].
- Keep CORS locked to the extension id and cookies tuned for cross-site as invariants of the proxy, not per-fix tweaks [1824].
- Keep the extension a thin client: every script calls the backend, never the provider directly, so keys and model choice never reach the browser [1825][1823].
- Make two-team key-isolation, session-expiry, and logout part of the pre-merge gate for any change to the proxy path, since that suite is what caught these defects [1827].
- Treat the shared proxy's per-team key isolation as the highest-consequence property to protect: it is the core risk of routing every team through one backend [1823][1827].
Ready to Implement This Playbook?
Our team can implement these strategies for you, tailored to your specific business needs.
Schedule Consultation