Skip to content

feat(auth): require sign-in on every install - #440

Merged
huyplb merged 1 commit into
mainfrom
feat/require-sign-in
Sep 29, 2026
Merged

huyplb merged 1 commit into
mainfrom
feat/require-sign-in

Conversation

@huyplb

@huyplb huyplb commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What changes

Every install now requires sign-in, and only an administrator creates accounts.

Before After
Personal install opened with no login First launch shows Create the administrator account; after that, sign-in
Multi-user mode: anyone could register POST /api/auth/register → 403; admins use Add user (POST /api/admin/users)
First SSO sign-in created an account SSO signs in existing accounts only
Unlimited sign-in attempts 20 per 15 minutes

Upgrade: on an install used before, setup claims the existing local account (or the bound APP_USER_EMAIL), so saved connections, history and workflows stay. A server already running with LOCAL_SINGLE_USER=false keeps its accounts and is never offered setup.

Setup code: a request from the machine Fox runs on (raw loopback socket, no forwarding headers) needs only a password. Anything else, including a reverse proxy, Docker port mapping, or the Vite dev proxy (now xfwd: true), must also enter a one-time code printed to the server log. The code is compared in constant time and stops working once setup is done; concurrent setups are serialized.

AUTH_REQUIRED is removed. LOCAL_SINGLE_USER now only marks a personal install, which gates machine-level routes (driver install, updates, host cloud credentials). The CLI resolves the same owner account through ownerAccount(). Metadata migration 19 adds users.password_set; it is append-only.

Verification

  • apps/web tsc, CLI tsc, e2e tsc: clean. npm run lint, lint:security and build all pass.
  • npx vitest run: 426 files, 5058 passed.
  • New tests:
    • auth.setup.test.ts: claim, bound email, race, explicit multi-user, CLI owner.
    • setup.routes.test.ts (real listener): a proxied caller needs the code; wrong code 403; right code works once, then 409; register 403; admin add-user, where a viewer gets 403 and no session gets 401.
    • AuthPage.test.tsx.
    • The contract test now asserts every non-public route refuses a request with no session.
  • In the browser against npm run dev:
    • Setup through the Vite proxy asks for the code, and a wrong code is refused.
    • After setup: sign-in page with no sign-up link.
    • No-session API calls get 401; setup with the right code gets 409; register gets 403.
  • E2E (they now sign in): smoke 3/3, SQL Editor 51/51, schema history 6/6, access 6/6, revert 4/4, revert edges 10/10, workflow 3/3, SQLite 6/6.

🤖 Generated with Claude Code


Note

High Risk
Mandatory authentication on all deployments changes default access for every user and install; setup claiming, session guards, and closed registration are security-critical paths that affect upgrades and exposed servers.

Overview
Every install now requires sign-in. The old open single-user mode and AUTH_REQUIRED are gone; all API routes use the session guard except public auth, signup, and health endpoints.

First-run setup replaces anonymous boot and self-registration: GET/POST /api/auth/setup creates or claims the legacy local@foxschema.app account so existing connections and history survive upgrades. Remote or proxied callers (Docker, reverse proxy, Vite xfwd) must enter a one-time setup code from the server log. POST /api/auth/register returns 403; admins add users via POST /api/admin/users and a new Add user form in Access control. SSO only signs in pre-provisioned accounts. Sign-in/setup is rate-limited (20 per 15 min).

LOCAL_SINGLE_USER now only gates machine-level actions (drivers, updates), not whether login is required. The CLI uses ownerAccount() to align with the claimed install owner. The UI auth flow drops the fake local user: setup vs login on AuthPage, Sign out always visible. E2E and CI sign in via a cached session helper hitting the API port directly. Migration 19 adds users.password_set to track when setup can close.

Reviewed by Cursor Bugbot for commit 917ad58. Bugbot is set up for automated code reviews on this repo. Configure here.

Fox used to open straight into the workspace on a personal install, and a
multi-user server let anyone register. Now every install signs in, and only
an administrator creates accounts.

- First launch is setup: it creates the admin, or on an install used before
  claims the existing local account so connections and history stay. A
  bound APP_USER_EMAIL is used as-is.
- Setup from another machine (anything not a direct loopback request, so a
  reverse proxy or the Vite proxy counts as remote) needs a one-time code
  printed to the server log. Setup closes once an account can sign in, and a
  server already running multi-user is never offered it.
- /api/auth/register answers 403; admins add users (POST /api/admin/users and
  an Add user form). SSO signs in existing accounts only.
- Sign-in is rate-limited to 20 per 15 minutes.
- users.password_set (migration 19) records who can sign in.
- AUTH_REQUIRED is gone; LOCAL_SINGLE_USER now only marks a personal install
  for machine-level routes. The CLI resolves the same owner account.
- e2e suites sign in through a cached session (apps/e2e/.env); the nightly
  cloud workflow makes a password per run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_9a431d8d-1a91-4ec6-9111-587711f1dfb1)

@huyplb
huyplb merged commit 9739e82 into main Sep 29, 2026
12 checks passed
@huyplb
huyplb deleted the feat/require-sign-in branch September 29, 2026 19:16
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