Repository navigation
Conversation
There was a problem hiding this comment.
Changes requested. This recovers when an SSO proxy answers 401, but it also catches the 401s that OpenCode's own server sends, which the app uses for its sign-in screen. I couldn't test it end to end because I have no SSO proxy or real browser. The PR's test and packages/app typecheck pass. I also ran a quick probe of my own: a password-protected opencode serve that keeps returning 401 on /api/info. With the app's 10s health polling, the wrapper ran location.replace('/?auth_refresh=…') every 60s.
Public API: unchanged.
Concerns:
- Your own server's 401s are treated as an expired proxy session (inline). With a server password, the app relies on a same-origin 401 to show its sign-in screen. Now the page reloads every 60s and wipes what the user is typing there.
- Overlap with #52223. It tackles the same expired-proxy-session problem (#50481, #57649) and also sets
platform.fetchinweb.ts, so the two PRs conflict. It confirms the proxy is redirecting (probes/api/infowithredirect: "manual") before reloading, and reloads with a?reauth=1the service worker lets through, rather than removing the service worker. Adding 401 handling to that approach, with the same check that the 401 comes from the proxy rather than OpenCode, is probably simpler than keeping two separate wrappers. Coordinating there would help. - The service worker is removed on every recovery (inline). Offline/PWA support is lost until the next registration.
- The reload goes to a hardcoded
/(inline). It drops the current route and ignores a base path when the app is served under a prefix. - You note the SSO flow itself hasn't been tested end to end yet. A run behind Authentik or Authelia would show the 401 path actually ends up on the sign-in page.
There was a problem hiding this comment.
Any same-origin 401 sets this off, including OpenCode's own UnauthorizedError when the server has a password. The web app counts on that 401 (health.unauthorized) to show its sign-in screen. The health check repeats every 10s, so the 60s limit on line 11 becomes a reload once a minute instead of a loop. Each reload clears the sign-in screen and sends the user to /. I reproduced this with a mocked 401 /api/info: 3 reloads in 3 minutes.
Could this only act when the 401 clearly comes from the proxy? For example: no UnauthorizedError JSON body, or a Location header like Authelia sends (#57649), or a check like #52223's /api/info probe.
This removes the service worker for good, so offline/PWA support is gone until it registers again. #52223 gets the same result without removing it: it adds a query parameter (reauth) and keeps that parameter out of the service worker's navigation fallback in vite.pwa.ts, so that one navigation goes to the network.
Going to a hardcoded / drops the route the user was on and breaks when the app is served under a path prefix. Reloading the current URL with an extra query parameter would keep both.
There was a problem hiding this comment.
Thanks for the update. Recovery now stays off after one attempt until /api/info succeeds again. I re-ran my probe of a password-protected opencode serve returning 401: the reload every 60s is gone and it now reloads once. The PR's test and packages/app typecheck pass. I still couldn't test the real SSO flow.
Public API: unchanged.
The earlier thread is still open. A 401 from OpenCode's own password check still costs one reload per tab, which drops the user's route. The service worker is still removed on every recovery. The reload still goes to a hardcoded /. The new ^/api check also means recovery quietly does nothing when the app is served under a path prefix.
The test has grown into one 72-line parameterised case, including a hand-built opaqueredirect response. It's hard to follow. A few small cases would be clearer: 401 triggers one reload, opaque redirect triggers one reload, other-origin and non-/api requests are ignored, and a healthy /api/info re-arms recovery.
It's also still worth coordinating with #52223, which handles the same expiry problem and also sets platform.fetch.

Issue for this PR
No separate issue. Related: #57168 (optional service authentication for use behind an authentication proxy). This PR does not depend on that change.
Type of change
What does this PR do?
OpenCode can run behind a reverse proxy with single sign-on (SSO). Examples include an Authentik forward-auth setup or Cloudflare Access.
When the SSO session expires, the proxy can return HTTP 401 for API requests. The page stays open, but API requests fail. I found this problem on mobile with Authentik. A normal page reload, even with a new query parameter, did not restore access. I had to clear the browser cache.
The daemon already sends
Cache-Control: no-cachefor HTML. But the service worker can serve cached HTML for page navigation. That navigation does not reach the proxy to start sign-in.On a same-origin HTTP 401, remove the service worker registration. Then navigate to the homepage with a new query parameter. This lets the document request reach the SSO proxy. Keep a 60-second limit in session storage to prevent a reload loop. Do not change requests to other origins or the desktop client.
This change handles HTTP 401 responses. It does not handle a proxy that returns a sign-in page with HTTP 200 or causes a CORS error.
How did you verify your code works?
bun run checkpassed.latestbinary with this change and feat(cli): add no-auth service support #57168.Screenshots / recordings
Not included. This change adds no visual controls. It starts a full-page navigation after an API authentication failure.
Checklist
Added by the opencode bot (AI-generated).
Related: #57649, #50481