fix(app): recover from expired proxy authentication by chetan · Pull Request #57834 · anomalyco/opencode · GitHub
Skip to content

fix(app): recover from expired proxy authentication - #57834

Open
chetan wants to merge 2 commits into
anomalyco:devfrom
chetan:sso-refresh
Open

chetan wants to merge 2 commits into
anomalyco:devfrom
chetan:sso-refresh

Conversation

@chetan

@chetan chetan commented Oct 21, 2026 •

Copy link
Copy Markdown

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

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

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-cache for 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 check passed.
  • The regression test passed in 10 consecutive runs. It checks same-origin recovery, service worker removal before navigation, concurrent failures, the reload limit, and unchanged successful responses.
  • Built and installed a local latest binary with this change and feat(cli): add no-auth service support #57168.
  • The SSO sign-in flow has not been verified end to end with the new binary. Cloudflare Access has not been tested.

Screenshots / recordings

Not included. This change adds no visual controls. It starts a full-page navigation after an API authentication failure.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #57649, #50481

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.fetch in web.ts, so the two PRs conflict. It confirms the proxy is redirecting (probes /api/info with redirect: "manual") before reloading, and reloads with a ?reauth=1 the 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.

Related: #57649, #50481

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

This branch has not been deployed

No deployments
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