anomalyco / anomalyco/opencode
web: native Basic auth prompt re-fires every second while entering credentials
@Brendonovich is already working on this.
Since Sep 3, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
When a browser opens a password-protected V2 web UI without credentials, the app shell now loads and boots unauthenticated while the event stream client reconnects to /api/event every second (reconnectDelay = 1_000 in packages/client/src/solid/connection.ts). Every reconnect returns 401 with WWW-Authenticate: Basic realm="Secure Area" (packages/server/src/middleware/authorization.ts). In Chromium-based browsers, each challenged request re-raises the native HTTP Basic auth prompt: while the user is typing the password, the dialog keeps being re-triggered roughly every second — it looks like screen flicker and keeps interrupting input. Firefox shows the prompt only once and is not visibly affected.
This became reachable with #46702 (merged 2026-09-02, 46c33630b7, "fix(server): authenticate only API requests"): authentication now applies only to /api, /api/*, and /openapi.json, so the HTML shell is public and the app starts issuing unauthenticated API requests before any credential exists. Before that commit, the document request itself was challenged once at navigation, so the native prompt appeared a single time before the app booted. The other two ingredients are older: the event-stream reconnect loop (dd296f7033, 2026-02-12, initially 250 ms) and the WWW-Authenticate challenge on API 401s (101566131d added it to UI fallback responses; 8cbc43fbb0, 2026-05-09 extended it to typed API 401s, which is what /api/event returns). The reconnect delay has been 1 s since 154f298fe9 (2026-08-13, #41930).
OpenCode version
v0.0.0-beta-18866
Steps to reproduce
- Run
opencode servein the foreground without a configured password (V2 generates a random one and printsserver password ...). - Open
http://127.0.0.1:<port>/in Chrome. The app shell renders, then the native Basic auth dialog appears. Firefox also shows the dialog, but only once — no flicker there. - Watch the network tab:
GET /api/eventfires about once per second, each returning401withWWW-Authenticate: Basic(alongside periodic/api/health401s). Measured with headless Chromium against the same server: 12 ×401on/api/eventin 12 s. Headless suppresses native auth prompts, so the dialog behavior needs a real browser. - Start typing the password into the native dialog: the dialog is re-triggered by the incoming 401s and input keeps getting interrupted.
Plugins
none
Operating System
Linux (Ubuntu), x64
Additional context
- Verified with a direct connection to the server (no proxy in between); an injected-CSS proxy behind a tunnel was ruled out as the cause.
- The server in this environment tracks the beta channel and already includes #46702; the symptom is observed on every login on this build.
- Related closed issues: #19920 (V1: repeated prompts from a WebSocket URL bug) and #16493 (V1:
/global/event401 crash) predate the auth challenge; #31484 (static-asset 401s breakauth_token) shares the same challenge mechanism, just on a different request surface. - Browsers tested (real devices, Android 17 on Pixel 10 Build/CP2A.260805.005): Chrome 152.0.7977.64 re-fires the prompt roughly every second; Firefox 154.0.1 (Build #2016180583, GV 154.0.1-20260824154132) shows the prompt once with no flicker.
- Workaround: open the UI with
?auth_token=<URL-encoded base64 of "opencode:<password>">— the app then sends its ownAuthorizationheader and the native prompt never appears. URL-encoding matters: the app reads the token withURLSearchParams, which turns a bare+into a space.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.