Skip to content

feat(web): HTTP Basic Auth for the web UI#181

Merged
tickernelz merged 1 commit into
tickernelz:mainfrom
GCS-ZHN:feat/web-http-auth-pr
Jul 21, 2026
Merged

feat(web): HTTP Basic Auth for the web UI#181
tickernelz merged 1 commit into
tickernelz:mainfrom
GCS-ZHN:feat/web-http-auth-pr

Conversation

@GCS-ZHN

@GCS-ZHN GCS-ZHN commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

When the web UI is bound to a non-loopback address (e.g. 0.0.0.0 so it is reachable from the LAN), any browser on the same network can otherwise read and edit memories directly. This PR gates the UI on HTTP Basic Auth so the obvious threat model is closed off by default.

What changes:

  • New config fields webServerAuthPassword / webServerAuthUsername turn the gate on. Username defaults to the current OS user; password accepts the same env:// and file:// shorthands as memoryApiKey.
  • src/services/web-auth.ts parses Authorization: Basic …, compares credentials with timingSafeEqual, and serves a 401 + WWW-Authenticate challenge with Cache-Control: no-store.
  • src/services/cors.ts now accepts non-loopback browser origins when auth is on, so tools hosted on other origins can talk to the API once they authenticate. /api/health is unauthenticated and is reused as the health probe by checkServerAvailable.
  • The frontend polls /api/health on load; if authEnabled is false and the page is served from a non-loopback hostname it shows a short banner pointing at webServerAuthPassword.
  • Tests cover the credential check, the challenge format, and the new CORS option.

Notes:

  • The branch is rebased onto upstream/main so the diff is just the auth feature (no memory-xml-markers mix-in).
  • Browser behaviour for the threat model: the browser caches Basic Auth credentials in memory, and closing all browser windows discards them. Reopening the browser requires signing in again.

When the web server is bound to a non-loopback address (e.g. 0.0.0.0 so
the UI is reachable from the LAN), any browser on the same network can
otherwise read and edit memories directly. This commit gates the UI on
HTTP Basic Auth so the obvious threat model is closed off by default.

What changed:

- webServerAuthPassword / webServerAuthUsername in opencode-mem.jsonc
  turn the gate on. Username defaults to the current OS user; password
  accepts the same env:// and file:// shorthands as memoryApiKey.
- src/services/web-auth.ts parses Authorization: Basic ..., compares
  credentials with timingSafeEqual, and serves a 401 + WWW-Authenticate
  challenge with Cache-Control: no-store.
- src/services/cors.ts now accepts non-loopback browser origins when
  httpAuthEnabled is true, so once auth is on, tools hosted on other
  origins can talk to the API. /api/health is unauthenticated and is
  reused as the health probe by checkServerAvailable.
- The frontend polls /api/health on load; if authEnabled is false and
  the page is being served from a non-loopback hostname it shows a
  short banner pointing at webServerAuthPassword.
- Tests cover the credential check, challenge format, and the new
  CORS option.
@tickernelz
tickernelz merged commit 80d305f into tickernelz:main Jul 21, 2026
1 check passed
@tickernelz

Copy link
Copy Markdown
Owner

Merged into main and included in the v2.20.0 release tag. Verified on the fully integrated batch tree with bun install --frozen-lockfile, bun run typecheck, bun run build, and bun test (226 pass / 0 fail). Thanks for the contribution!

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.

2 participants