Skip to content

fix(web): stop credential prompt loop with text/plain 401 + manifest credentials#24829

Open
rmk40 wants to merge 3 commits intoanomalyco:devfrom
rmk40:fix/web-auth-credential-loop
Open

fix(web): stop credential prompt loop with text/plain 401 + manifest credentials#24829
rmk40 wants to merge 3 commits intoanomalyco:devfrom
rmk40:fix/web-auth-credential-loop

Conversation

@rmk40
Copy link
Copy Markdown
Contributor

@rmk40 rmk40 commented Apr 28, 2026

Issue for this PR

Closes #18325

Type of change

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

What does this PR do?

When OPENCODE_SERVER_PASSWORD is set, the basic-auth dialog flashes
2-3 times before the password field accepts input. Two independent
causes; both fixed here.

1. 401 response has no Content-Type. hono/basic-auth builds
its 401 with only WWW-Authenticate set. Bun fills the missing slot
with application/octet-stream, and Chromium briefly treats the
unauth response as a download attachment — that race produces the
flash-and-reset. AuthMiddleware now adds text/plain; charset=utf-8
to a 401 with no Content-Type before re-throwing.

2. Webmanifest is fetched without page credentials. <link rel="manifest"> is fetched in a no-CORS mode that does not include
basic-auth credentials. Even after authenticating, the browser issues
a separate dialog for the manifest and logs Manifest fetch ... failed, code 401. Adding crossorigin="use-credentials" makes the
fetch carry the page's credentials. Same-origin, so no CORS preflight;
no effect when OPENCODE_SERVER_PASSWORD is unset.

How did you verify your code works?

From packages/opencode:

  • bun test test/server/auth-middleware.test.ts — 6/6 pass. The new
    test mounts AuthMiddleware on a minimal Hono app and asserts the
    401 carries text/plain (never octet-stream) and preserves
    WWW-Authenticate, plus authenticated / no-password / OPTIONS /
    auth_token paths. Confirmed it fails against pre-fix code.
  • bun typecheck clean.
  • curl -i http://localhost:<port>/ returns
    content-type: text/plain; charset=utf-8 on the 401.

Screenshots / recordings

N/A

Checklist

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

rmk40 added 3 commits April 28, 2026 10:47
hono's basicAuth middleware returns its 401 response without setting a
Content-Type header. Bun's HTTP serializer fills the missing slot with
`application/octet-stream`, which Chromium briefly treats as a
download-attachment trigger before settling on the basic-auth dialog.
The race produces 2-3 dialog flashes before the password field accepts
input, and Playwright surfaces it as `Error: Download is starting` on
the navigation call. Subresources (favicons, JS bundle, CSS, manifest)
each hit basic-auth independently and each get the same octet-stream
401, compounding the loop.

Wrap the basicAuth call in AuthMiddleware so a 401 response with no
Content-Type gets `text/plain; charset=utf-8` added before being
re-thrown. Authenticated requests are unaffected.

Verified by:
- `curl -i http://localhost:<port>/` now returns
  `content-type: text/plain; charset=utf-8` on the 401.
- `bun typecheck` and `bun test` clean from packages/opencode.
Web manifests are fetched in a no-CORS mode that does not include the
page's basic-auth credentials by default. With OPENCODE_SERVER_PASSWORD
set, an authenticated user still sees `Manifest fetch ... failed,
code 401` errors on every reload, and the browser issues a separate
auth dialog for the manifest before settling.

Adding `crossorigin="use-credentials"` makes the browser send the
page's credentials with the manifest request, so the manifest fetch
succeeds in the same auth context as the page.

When OPENCODE_SERVER_PASSWORD is unset (no auth), this attribute has
no effect on the request - the manifest is served unauthenticated as
before.
Add a regression test asserting AuthMiddleware's 401 response carries
`text/plain` Content-Type (never `octet-stream`) and preserves
`WWW-Authenticate`. The new test file exercises AuthMiddleware
directly through a minimal Hono app, covering:

- Missing credentials -> 401, Content-Type contains text/plain, never
  octet-stream, WWW-Authenticate present.
- Wrong credentials -> same shape.
- Correct credentials -> 200 passes through.
- No password set -> 200 passes through.
- OPTIONS preflight bypasses auth.
- auth_token query parameter accepted as credentials.

Verified the missing/wrong-credential tests fail against pre-fix code
and pass post-fix (6/6).

Closes anomalyco#18325
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential duplicate found:

Recommend reviewing PR #19302 to ensure no duplication of the manifest credentials fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opencode-web Basic Auth Bug

1 participant