Skip to content

feat: add API Key authentication for REST and WebSocket servers#53

Open
Vishnu-tppr wants to merge 1 commit into
Zen4-bit:mainfrom
Vishnu-tppr:feat/api-auth-security
Open

feat: add API Key authentication for REST and WebSocket servers#53
Vishnu-tppr wants to merge 1 commit into
Zen4-bit:mainfrom
Vishnu-tppr:feat/api-auth-security

Conversation

@Vishnu-tppr

Copy link
Copy Markdown

This change plugs a major security flaw in the local gateway: currently, any website you visit in a web browser can make silent cross-origin (CORS) HTTP/WebSocket calls to localhost:3210 and abuse the local AI models. Even worse, since the completions endpoint supports a "file" reference parameter, malicious websites could read arbitrary local files from the host machine.

To fix this, we are adding API Key authentication:

  • Auto-Generation: Generates a secure random 24-byte hex key in settings.json on the first run.
  • REST Auth: Restricts completions, models, and stats endpoints with Authorization: Bearer . Public pages (/docs, /api/status) bypass this so they remain readable.
  • WebSocket Auth: Validates the apiKey query parameter (?apiKey=) during the HTTP upgrade handshake and severs bad connections with a 401 before the WebSocket connection is established.
  • CLI Integration: Automatically reads settings.json from the OS AppData path to keep CLI usage zero-config.
  • Interactive Docs: Injects the key into the HTML templates so the live chat widget on /docs connects automatically.

@qodo-code-review

qodo-code-review Bot commented Jun 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0)

Grey Divider


Action required

1. Docs leak API key 🐞 Bug ⛨ Security
Description
The /, /docs, /cli, and /ws HTML pages embed the generated API key into inline JS while responding
with Access-Control-Allow-Origin: '*', so any website can fetch these pages cross-origin and extract
the key. With the stolen key, the attacker can authenticate to the protected REST/WS endpoints and
exfiltrate responses, reintroducing the original localhost abuse/file-read threat the PR is meant to
close.
Code

electron/rest-api.cjs[R1389-1406]

    // Docs page
    if (method === 'GET' && (pathname === '/' || pathname === '/docs')) {
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
-        res.end(getDocsPage());
+        res.end(getDocsPage(getApiKey ? getApiKey() : ''));
        return;
    }

    // CLI docs page
    if (method === 'GET' && pathname === '/cli') {
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
-        res.end(getCLIDocsPage());
+        res.end(getCLIDocsPage(getApiKey ? getApiKey() : ''));
        return;
    }
    // WebSocket docs page
    if (method === 'GET' && (pathname === '/ws' || pathname === '/websocket')) {
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
-        res.end(getWSDocsPage());
+        res.end(getWSDocsPage(getApiKey ? getApiKey() : ''));
        return;
Evidence
The docs endpoints are served with CORS '*' and now call getDocsPage(getApiKey()) /
getCLIDocsPage(getApiKey()) / getWSDocsPage(getApiKey()), which leads to embedding the API key
into inline JS. Since CORS allows any origin to read these HTML responses, a malicious site can
fetch the docs and steal the key. Once stolen, the server’s JSON responses are also CORS-readable
via sendJSON(), enabling full cross-origin data exfiltration.

electron/rest-api.cjs[1389-1406]
electron/rest-api.cjs[373-382]
electron/rest-api.cjs[122-130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The server returns docs HTML pages with `Access-Control-Allow-Origin: '*'` and now injects the secret `apiKey` into those pages. Any arbitrary website can `fetch('http://localhost:3210/docs')` and read the response body due to CORS, extract the API key, then call authenticated REST endpoints / open authenticated WebSockets.

## Issue Context
- Docs handlers explicitly set `Access-Control-Allow-Origin: '*'`.
- The docs rendering now passes `getApiKey()` into the template, and the embedded chat widget uses it to connect.
- `sendJSON()` also sets `Access-Control-Allow-Origin: '*'`, so once the key is stolen, responses become fully readable cross-origin.

## Fix Focus Areas
- electron/rest-api.cjs[1389-1406]
- electron/rest-api.cjs[373-382]
- electron/rest-api.cjs[122-130]

## Suggested fix approach
1. **Do not send `Access-Control-Allow-Origin: '*'` on the docs HTML routes** (`/`, `/docs`, `/cli`, `/ws`, `/websocket`). Ideally omit the CORS header entirely for HTML pages.
2. Consider also **restricting CORS for JSON endpoints** (replace `'*'` with an allowlist like `http://localhost:${REST_PORT}` and `http://127.0.0.1:${REST_PORT}`), or make it conditional on `req.headers.origin`.
3. If you must keep docs CORS-readable for some reason, then **do not embed the API key** in the HTML/JS; require manual entry or another same-origin-only mechanism.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add API key auth for local REST + WebSocket gateway (CLI + docs support)
✨ Enhancement 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

Walkthroughs

Description
• Generate and persist a per-install API key in settings.json on first run.
• Require Bearer auth for sensitive REST endpoints while keeping public status/docs readable.
• Enforce API key validation during WebSocket upgrade and auto-configure CLI/docs clients.
Diagram
graph TD
  EM["Electron main"] -->|"load/save"| ST[("settings.json")]
  EM -->|"init + getApiKey()"| RA["REST API server"] -->|"initWebSocket(getApiKey)"| WS["WebSocket server"]
  CLI["CLI client"] -->|"Authorization: Bearer"| RA
  RA -->|"inject apiKey into HTML"| DOC["Docs UI (/docs) "]
  DOC -->|"ws://.../ws?apiKey="| WS
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Origin allowlist + remove permissive CORS
  • ➕ Directly targets the described drive-by browser abuse vector
  • ➕ Avoids embedding secrets in HTML and URLs
  • ➕ No client key distribution/rotation concerns
  • ➖ Origin checks are brittle for non-browser clients and file:// scenarios
  • ➖ Does not protect against non-browser local processes calling localhost
  • ➖ Still needs an auth story if the API is intended for third-party tools
2. Session/CSRF-style token tied to a local UI login
  • ➕ Avoids long-lived static keys; easier to revoke/expire
  • ➕ Better browser security model (same-site cookies + CSRF protection)
  • ➕ Can support multiple concurrent clients/users
  • ➖ More moving parts (sessions, expiry, storage)
  • ➖ Adds UX complexity for “zero-config” CLI and scripts
  • ➖ Still requires some bootstrapping path for headless use
3. Use OS-protected local transport (UDS / named pipe) or mTLS
  • ➕ Strong boundary: only local authorized processes can connect
  • ➕ Avoids bearer secrets in query strings and headers
  • ➕ Reduces exposure if CORS is misconfigured
  • ➖ Cross-platform complexity (Windows vs macOS/Linux)
  • ➖ Harder to debug and integrate with generic HTTP tooling
  • ➖ Bigger architectural shift than this PR’s scope

Recommendation: The API-key approach in this PR is a pragmatic, low-friction fix that immediately blocks unauthenticated REST and WS access while keeping CLI/docs usable. Consider follow-up hardening to also reduce browser attack surface (e.g., tighten/disable wildcard CORS and/or implement Origin allowlisting), since embedding the key into docs HTML and passing it via WS query string improves usability but increases accidental disclosure risk (logs, screenshots, shared HTML).

Grey Divider

File Changes

Enhancement (2)
proxima-cli.cjs Auto-read API key and attach Bearer auth to CLI requests +37/-2

Auto-read API key and attach Bearer auth to CLI requests

• Adds a helper to resolve the API key from PROXIMA_API_KEY or the OS-specific settings.json path. Updates the HTTP request helper to include an Authorization: Bearer header when a key is available and preserves URL query strings in requests.

cli/proxima-cli.cjs


main-v2.cjs Generate/persist API key in settings and expose via IPC +15/-2

Generate/persist API key in settings and expose via IPC

• Introduces crypto-based apiKey generation when settings.json is missing the key (including first-run). Exposes a getApiKey IPC handler so other components (e.g., REST/WS servers) can retrieve the current key.

electron/main-v2.cjs


Bug fix (2)
rest-api.cjs Protect REST routes with Bearer auth and inject key into docs chat widget +24/-13

Protect REST routes with Bearer auth and inject key into docs chat widget

• Threads a getApiKey callback into the REST server and adds an authorization gate for non-public routes (leaving /docs, /api/status, and related pages open). Updates docs/CLI/WS HTML generators so the embedded chat widget connects with ws://.../ws?apiKey=<key>, and passes getApiKey through to the WebSocket initializer.

electron/rest-api.cjs


ws-server.cjs Validate API key during WebSocket upgrade handshake +12/-1

Validate API key during WebSocket upgrade handshake

• Extends WebSocket initialization to accept an apiKey provider function and checks the apiKey query parameter during the HTTP upgrade. Rejects invalid/missing keys with an HTTP 401 and closes the socket before establishing a WebSocket connection.

electron/ws-server.cjs


Grey Divider

Qodo Logo

Comment thread electron/rest-api.cjs
@Vishnu-tppr
Vishnu-tppr force-pushed the feat/api-auth-security branch from eee592d to 688fced Compare June 9, 2026 16:52
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