Skip to content

feat(auth): opt-in authentication & multi-user segregation#1930

Merged
jcfs merged 20 commits into
mainfrom
feature/opt-in-setup-authent-95z
Jul 24, 2026
Merged

feat(auth): opt-in authentication & multi-user segregation#1930
jcfs merged 20 commits into
mainfrom
feature/opt-in-setup-authent-95z

Conversation

@jcfs

@jcfs jcfs commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Opt-in Authentication & Multi-User Segregation

Kandev has shipped as a single-user local tool with no authentication — the backend binds 0.0.0.0 by default and anyone who can reach the port owns the instance and every credential on it. This PR adds opt-in user authentication and hard per-user data isolation, so a team can share one Kandev server safely, while laptop installs are completely unchanged (auth is OFF by default).

Spec: docs/specs/auth/spec.md · ADR: docs/decisions/2026-07-24-opt-in-authentication.md · Docs: docs/public/authentication.md

How it works

  • Enablement is a runtime feature toggleSettings > System > Feature Toggles > "Authentication & users" (or KANDEV_FEATURES_AUTH=true). No separate settings section. Mode derives from the flag: off → disabled, on + no admin → setup wizard, on + admin → enabled.
  • Disabled mode injects a synthetic single-user admin identity, so all downstream code is identity-aware while behavior stays byte-identical to today.
  • Credentials: argon2id passwords (PHC-encoded, 64 MiB / t=1 / p=4, per-user salt, constant-time verify). Opaque DB-backed session cookies (HttpOnly, SameSite=Lax, Secure on TLS, 30-day sliding, instantly revocable) and personal access tokens (kandev_pat_…) for CLI/MCP/scripts. Only SHA-256 digests are stored.
  • Per-user workspaces (hard isolation): each workspace has an owner; users see only their own. Admins do not see other users' workspaces (management role, not visibility). Setup promotes the existing single-user row to admin and claims pre-auth data.
  • Accounts: first-run setup wizard, admin-managed users + tokenized invite links, roles (admin/member), last-admin guard, disable-revokes-everything.

What is isolated

When auth is on, everything in a workspace is private to its owner and returns 404 to anyone else — even if they know the ID: workspaces, tasks, workflows, sessions, plans, walkthroughs, terminals, VS Code, port previews, git snapshots, session turns/context, secrets, third-party integration settings (GitHub/GitLab/Jira/Linear/Sentry/Slack/Azure), and automations (incl. webhook secrets). WebSocket subscriptions and broadcasts are owner-scoped too.

Known limit: the filesystem and agent-CLI credentials (gh auth, claude login, provider keys) authenticate as the shared OS user under one ~/.kandev tree — that's application-layer isolation, not OS sandboxing. Integration PATs that flow through Kandev's secret store are per-user; CLI-binary logins are not. Documented in the spec/public docs.

Scope of change

  • New apps/backend/internal/auth/ (identities, sessions, PATs, invites, argon2id, rate limiter, mode machine, enforcement middleware, HTTP API).
  • Per-user scoping across the task service, secrets, office, the WS gateway, integrations, automation, plans, walkthroughs, and session-read paths.
  • Frontend: auth store slice, credentialed fetch + 401 handling, pre-auth gate, login/setup/invite pages, Settings > System > Users + Settings > Account (password, sessions, API tokens), and a current-user chip in the sidebar footer.

Verification

  • Two independent security audits run during development; every finding fixed (3 IDORs on terminal/vscode/port proxies, cross-user integration/automation/plan/session reads, a login rate-limit bypass, an invite race). Regression tests added for each.
  • Dedicated Playwright auth project incl. the acceptance test: two users fully segregated over HTTP, boot payload, and WebSocket (asserts no leak, including integration routes). Green.
  • Full backend suite: no new failures (only pre-existing sandbox-FS/gitlab environment failures). Web typecheck + lint (0 warnings) + vitest green.

Follow-ups (not in this PR)

  • SSO (OIDC/SAML) — the auth_identities(provider, subject) schema is already provider-agnostic; SSO is a new front door that ends at the same createSession.
  • MFA/TOTP — fits the same pre-session seam.
  • Per-user agent-credential / filesystem isolation (OS-level; separate effort).

🤖 Generated with Claude Code


Screenshots

Enable it in Feature Toggles (no separate section) → setup wizard → login

Feature Toggles (auth control) Setup wizard Login
feature toggles setup login

Admin: users & invites

Users Invite link Account · password & sessions API token (shown once)
users invite account security token

Segregation + current-user chip — the member sees only their own workspace (not the admin's), with the right-aligned user chip in the sidebar footer (border hover, no accent fill):

Member's segregated view Chip hover
member chip

Screenshots live on an unmerged media/pr-1930-screenshots ref (SHA-pinned), so no binaries reach main.

Kandev Agent and others added 17 commits July 24, 2026 11:48
… state machine

Phase 1 of opt-in authentication + multi-user segregation:
- internal/auth: argon2id password hashing (PHC), opaque session/PAT tokens
  (SHA-256 at rest), login rate limiter, auth-mode state machine
  (disabled/setup/enabled), setup-wizard promotion of the pre-auth
  default-user row into the admin account, invites, admin user management
  with last-admin guard.
- internal/auth/store: auth_identities (OIDC-ready), auth_sessions,
  auth_api_tokens, auth_invites tables (SQLite+Postgres dialects).
- internal/auth/authn: request-identity plumbing (gin + plain contexts),
  RequireAdmin middleware.
- users table: display_name/role/status columns via new MigrateLogger
  wiring; AccountRepository surface (consumer-scoped, keeps existing
  Repository fakes untouched).
- config: AuthConfig.Required/SessionTTLHours/CookieName
  (KANDEV_AUTH_REQUIRED), FeaturesConfig.Auth; profiles.yaml + runtime
  flag registry entry for KANDEV_FEATURES_AUTH.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t payload

Phase 2 of opt-in authentication + multi-user segregation:
- internal/auth/httpmw: global gin middleware after CORS. Disabled mode
  injects the synthetic single-user admin identity (behavior unchanged);
  enabled mode resolves session cookie then PAT bearer, with an explicit
  allowlist (health, SPA bootstrap reads, credential endpoints,
  self-authenticating webhooks), deferral for WS/proxy upgrades and
  office agent JWTs, and SPA shell availability for the login page.
- internal/auth/httpapi: full auth surface — setup wizard, login/logout,
  me, password change, sessions, PATs, invites (mint/preview/accept),
  auth settings (enable/disable), admin user management.
- Boot payload: initialState.auth block always present; unauthenticated
  visitors on auth-enabled instances get features+auth only (no data).
- External MCP: PAT required once auth is enabled (open while disabled).
- Admin guards on destructive system routes and runtime-flag overrides.
- Startup warning when bound non-loopback with auth disabled
  (ServerConfig.NonLoopbackBinds fail-closed nudge).
- Wiring: auth store in Repositories, auth service in Services,
  ClaimUnownedWorkspaces backfill on the task repository.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 3 of opt-in authentication + multi-user segregation:
- Task service: workspaces, workflows, repositories, and tasks are scoped
  to the request identity (owner-only visibility; pre-auth unowned rows
  stay shared). Denials use *NotFound sentinels - no existence leak.
  Internal callers and the disabled-mode synthetic identity remain
  unscoped, preserving behavior exactly when auth is off.
- CreateWorkspace stamps the authenticated caller as owner.
- Session chokepoint: lifecycle Manager.GetOrEnsureExecution runs a
  settable session-access check covering shell/files/ports/vscode/LSP.
- Secrets: per-user user_id column, scoped CRUD/list, ClaimUnowned wired
  into the setup-wizard backfills.
- Boot payload inherits scoping automatically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 4 of opt-in authentication + multi-user segregation:
- Connection auth: /ws, /terminal, /lsp, /vscode, /port-proxy routes are
  guarded when auth is enabled - session cookie (same-origin upgrade),
  resolved identity from the HTTP middleware, or ?token=<PAT> for
  headerless programmatic clients; JSON 401 before any upgrade.
- Client identity: every WS client carries its authn.Identity; dispatched
  RPC actions (workspace.list, task CRUD, ...) run under a context
  carrying it, so WS RPC gets the same per-user scoping as HTTP.
- Subscription checks: task/session subscribe+focus verify workspace
  visibility via the task service; user topic subscriptions are pinned to
  the client's own user.
- Broadcast routing: new Hub.BroadcastToWorkspace routes workspace-scoped
  events (task/workflow/repo/office/session-state) to the owner's clients
  only, with safe fallbacks (unowned/unresolvable -> broadcast) so events
  never vanish; instance-wide events (executors, environments, agent
  profiles, system jobs) stay global by design.
- Replaced the long-standing discarded-token TODO in HandleConnection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n auth

- docs/specs/auth/spec.md: product behavior (opt-in, setup wizard,
  per-user workspaces, sessions/PATs, invites, public surface, v1 limits)
- apps/backend/AGENTS.md: internal/auth package layout + scoping rules
  for new service entry points and WS broadcast call sites

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New docs/public/authentication.md: enabling, users & invites, PATs,
  public endpoints, limitations, disabling
- run-as-a-service.md: network-security note now points at the real
  authentication boundary instead of reverse-proxy-only guidance
- meta.json: nav entry under Configure Kandev

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the load-bearing choices: mode state machine + synthetic identity
in disabled mode, opaque DB-backed credentials over JWTs, default-user
promotion at setup, service-layer scoping with no-admin-bypass, and the
explicit public allowlist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ttings

Adds the frontend half of opt-in authentication: a pre-auth gate in
main.tsx that renders the setup wizard or login/invite pages instead of
the app shell whenever the boot payload's auth block says so, a new
`auth` store slice + feature flag mirroring the backend's StatePayload,
credentialed fetch (`credentials: "include"`) with a 401 handler that
clears the session and redirects to /login, and a full auth-api.ts
client for the /api/v1/auth and /api/v1/users routes.

Settings additions: a System > Users page (list/create/disable/promote
users, invite-link dialog) and System > Authentication page (enable/
disable with confirm + env-locked banner), gated on the `auth` feature
flag and admin role. A new Account sidebar group (Profile & Password,
API Tokens) is shown to signed-in users once auth is enabled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New dedicated Playwright auth project (backend restarts with
KANDEV_AUTH_REQUIRED=true, isolated from the tokenless default suite):
setup-mode boot gating, admin setup wizard, login/logout + bad
credentials, PATs, admin-surface authorization, anonymous WS rejection,
UI login flow, and the acceptance test - two-user segregation across
HTTP, boot payload, and WebSocket.

The segregation spec immediately caught a real leak: workspace.* event
payloads carry their ID as 'id' (the payload IS the workspace DTO), not
'workspace_id', so BroadcastToWorkspace fell back to broadcast-to-all
for workspace create/update/delete. Fixed with action-aware extraction
plus a unit regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two independent security audits of the auth branch found several
authorization gaps where session/workspace-scoped surfaces were not wired
to the new per-user chokepoint. All fixed:

- IDOR on reverse-proxy/terminal routes (vscode, port-proxy, terminal):
  these resolve executions via GetExecutionBySessionID /
  GetOrEnsureExecutionForEnvironment / EnsurePassthroughExecution, which
  skipped the session-access check that only GetOrEnsureExecution ran. Added
  the check inside the env/passthrough resolvers (before the cache
  short-circuit) and an explicit CheckSessionAccess gate in the two bare-lookup
  proxy handlers. An authenticated non-owner can no longer reach another
  user's IDE, forwarded ports, or shell.
- Cross-user agent-conversation read: ListMessages / ListMessagesPaginated /
  SearchMessages now authorize the session.
- Cross-user repository-script read/inject: script CRUD now authorizes the
  parent repository's workspace.
- Office HTTP surface: a group middleware enforces workspace ownership on
  :wsId routes for real users (agent-JWT callers keep their claim scoping).
- Login rate-limit bypass: SetTrustedProxies(nil) so a spoofed
  X-Forwarded-For can't rotate the limiter key on a directly-reachable
  backend.
- Invite double-provisioning race: MarkInviteUsed is now the atomic
  serialization point (consumed before user creation).
- Setup wizard: serialized with a mutex so concurrent first-visitors can't
  corrupt the promoted admin profile.
- Log sanitizer: explicit kandev_pat_ redaction rule.

Regression tests added for message/script scoping and the
execution-access-before-cache invariant. Auth E2E (incl. two-user
segregation) still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET /api/v1/repositories/:id/active-session-count went through
CountActiveSessionsByRepository, which was not identity-scoped - a
cross-user info leak of another owner's active session counts. Added the
same authorizeRepositoryID gate the script routes use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…en UI

The auth store structs (APIToken, Invite, LoginIdentity, Session) had db:
tags but no json: tags, so the handlers that serialize them directly sent
Go-cased keys (CreatedAt, Name) AND leaked the SHA-256/password digests to
clients. Two consequences:
- The API-tokens UI showed 'Invalid Date' and an empty name (frontend reads
  created_at/name).
- token_sha256 / password_hash were serialized in list responses.

Added json tags with snake_case keys the frontend reads and json:"-" on
every credential digest. Regression test asserts digests never serialize and
the snake_case timestamp/name keys are present. Also adds the
auth-screenshots capture spec (artifacts gitignored).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n sidebar

Remove the standalone Authentication settings page and its dedicated
settings/get-auth-settings API surface now that enabling auth lives in the
Feature Toggles system, and drop env_required now that /auth/me no longer
returns it. Add a current-user chip with a logout action to the sidebar
footer, shown only in enabled auth mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per review feedback: authentication configuration now lives entirely in
the existing Feature Toggles system rather than a separate Authentication
settings section.

- The auth mode is derived from the features.auth runtime flag
  (KANDEV_FEATURES_AUTH): off -> disabled, on+no-admin -> setup,
  on+admin -> enabled. Removed the parallel auth.mode system setting,
  the SetEnabled service method, GET/PATCH /api/v1/auth/settings, the
  AuthConfig.Required field, KANDEV_AUTH_REQUIRED, and the auth service's
  Settings-store dependency and EnvRequired/env_required.
- The features.auth registry entry is re-described as the actual enable
  switch (high-risk, restart-required) and defaults off in every profile
  (dev was previously true — that only revealed UI before; now it would
  enforce, so it must be an explicit opt-in).
- Setup no longer persists a mode; completing the wizard creates the admin
  and the mode auto-derives to enabled.
- Startup 'exposed without auth' warning + all docs (public guide,
  run-as-a-service, spec, ADR, AGENTS.md) point at the feature toggle.
- E2E: auth specs use KANDEV_FEATURES_AUTH; screenshot spec captures the
  Feature Toggles page instead of the removed Authentication page; the
  member-restriction assertion targets an admin invite route.
- Tests updated across service/httpmw/httpapi/profiles for the derived
  mode; full suite green (only pre-existing sandbox-FS failures remain).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plan/walkthrough, session reads

A follow-up isolation audit found that per-user scoping had been wired into
the task service, secrets, office, and the WS gateway — but several
workspace/task/session-scoped surfaces still resolved a caller-supplied ID
with no ownership gate. Closed all of them; cross-user access now returns
404 across the board.

- Integrations (jira/linear/sentry/slack/azure-devops/gitlab/github/
  workflow-sync): a global middleware authorizes the workspace_id (query, or
  gitlab's /workspaces/:id/ path) on those route groups. Previously a member
  who knew another user's workspace ID could read/overwrite their integration
  connection config and watches.
- Automation: workspace-authorizer seam gates get/list/create/update/delete/
  enable/disable, triggers, run history, and — critically — the webhook
  secret. Was fully cross-tenant over WS before.
- Task plans (PlanService) and walkthroughs (WalkthroughService): task-
  authorizer seam on every read/write. These bypassed the task service.
- Session reads that hit the repo directly — session turns, task context,
  git snapshots, file reviews — now call AuthorizeSession/TaskAccess.

Internal callers (pollers, schedulers, MCP) carry no identity and stay
unscoped; disabled mode is unaffected. Regression tests added
(plan/walkthrough scoping, automation authorizer matrix, integration path
extraction) and the two-user E2E now asserts integration routes are shielded.
Docs (spec, ADR, public guide) updated to state what is isolated and that
filesystem/agent-credential isolation remains an OS-level limitation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sidebar current-user chip used hover:bg-accent (a purpleish fill).
Switch to the border-hover idiom used elsewhere (border border-transparent
-> hover:border-border) with the neutral hover:bg-muted/50 the adjacent
workspace picker uses, so the hover reads as a subtle outline instead of a
colored background. Screenshot spec now captures the chip hover state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Push the user chip to the far right of the expanded footer (ml-auto) so it
sits opposite the left-aligned icon buttons; collapsed mode keeps the
centered vertical stack unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

Too many files changed for review. (124 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 124 files, which is 24 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5648a0a2-f043-4b28-84bc-91955043f6d9

📥 Commits

Reviewing files that changed from the base of the PR and between f85b648 and 3301c57.

📒 Files selected for processing (124)
  • .gitignore
  • apps/backend/AGENTS.md
  • apps/backend/internal/agent/runtime/lifecycle/manager.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_access_test.go
  • apps/backend/internal/agent/runtime/lifecycle/manager_execution.go
  • apps/backend/internal/agent/runtime/routingerr/sanitize.go
  • apps/backend/internal/auth/authn/identity.go
  • apps/backend/internal/auth/httpapi/cookie.go
  • apps/backend/internal/auth/httpapi/handlers.go
  • apps/backend/internal/auth/httpapi/handlers_admin.go
  • apps/backend/internal/auth/httpapi/handlers_test.go
  • apps/backend/internal/auth/httpmw/middleware.go
  • apps/backend/internal/auth/httpmw/middleware_test.go
  • apps/backend/internal/auth/password.go
  • apps/backend/internal/auth/password_test.go
  • apps/backend/internal/auth/ratelimit.go
  • apps/backend/internal/auth/ratelimit_test.go
  • apps/backend/internal/auth/service.go
  • apps/backend/internal/auth/service_credentials.go
  • apps/backend/internal/auth/service_invites.go
  • apps/backend/internal/auth/service_setup.go
  • apps/backend/internal/auth/service_test.go
  • apps/backend/internal/auth/service_users.go
  • apps/backend/internal/auth/state.go
  • apps/backend/internal/auth/store/models.go
  • apps/backend/internal/auth/store/schema.go
  • apps/backend/internal/auth/store/store.go
  • apps/backend/internal/auth/store/store_test.go
  • apps/backend/internal/auth/tokens.go
  • apps/backend/internal/auth/tokens_test.go
  • apps/backend/internal/automation/service.go
  • apps/backend/internal/automation/service_test.go
  • apps/backend/internal/automation/store.go
  • apps/backend/internal/backendapp/auth.go
  • apps/backend/internal/backendapp/boot_state.go
  • apps/backend/internal/backendapp/gateway.go
  • apps/backend/internal/backendapp/helpers.go
  • apps/backend/internal/backendapp/helpers_test.go
  • apps/backend/internal/backendapp/integration_scope_test.go
  • apps/backend/internal/backendapp/main.go
  • apps/backend/internal/backendapp/services.go
  • apps/backend/internal/backendapp/storage.go
  • apps/backend/internal/backendapp/types.go
  • apps/backend/internal/common/config/config.go
  • apps/backend/internal/common/config/config_test.go
  • apps/backend/internal/gateway/websocket/access.go
  • apps/backend/internal/gateway/websocket/access_test.go
  • apps/backend/internal/gateway/websocket/client.go
  • apps/backend/internal/gateway/websocket/handler.go
  • apps/backend/internal/gateway/websocket/hub.go
  • apps/backend/internal/gateway/websocket/office_notifications.go
  • apps/backend/internal/gateway/websocket/office_notifications_test.go
  • apps/backend/internal/gateway/websocket/port_proxy.go
  • apps/backend/internal/gateway/websocket/run_notifications_test.go
  • apps/backend/internal/gateway/websocket/setup.go
  • apps/backend/internal/gateway/websocket/task_notifications.go
  • apps/backend/internal/gateway/websocket/vscode_proxy.go
  • apps/backend/internal/profiles/profiles.yaml
  • apps/backend/internal/profiles/profiles_test.go
  • apps/backend/internal/runtimeflags/config.go
  • apps/backend/internal/runtimeflags/handlers.go
  • apps/backend/internal/runtimeflags/handlers_test.go
  • apps/backend/internal/runtimeflags/registry.go
  • apps/backend/internal/secrets/sqlite_store.go
  • apps/backend/internal/secrets/sqlite_store_test.go
  • apps/backend/internal/system/system.go
  • apps/backend/internal/task/handlers/task_context_handler.go
  • apps/backend/internal/task/handlers/task_git_handlers.go
  • apps/backend/internal/task/handlers/task_http_handlers.go
  • apps/backend/internal/task/repository/sqlite/workspace.go
  • apps/backend/internal/task/service/plan_service.go
  • apps/backend/internal/task/service/service_access.go
  • apps/backend/internal/task/service/service_access_test.go
  • apps/backend/internal/task/service/service_messages.go
  • apps/backend/internal/task/service/service_resources.go
  • apps/backend/internal/task/service/service_tasks.go
  • apps/backend/internal/task/service/walkthrough_service.go
  • apps/backend/internal/user/models/models.go
  • apps/backend/internal/user/store/sqlite.go
  • apps/backend/internal/user/store/store.go
  • apps/web/app/auth/invite-page.tsx
  • apps/web/app/auth/login-page.tsx
  • apps/web/app/auth/setup-wizard.tsx
  • apps/web/components/app-sidebar/app-sidebar-footer.test.tsx
  • apps/web/components/app-sidebar/app-sidebar-footer.tsx
  • apps/web/components/app-sidebar/current-user-chip.tsx
  • apps/web/components/app-sidebar/sections/settings/account-group.tsx
  • apps/web/components/app-sidebar/sections/settings/settings-tree-render.test.tsx
  • apps/web/components/app-sidebar/sections/settings/settings-tree.tsx
  • apps/web/components/app-sidebar/sections/settings/system-group.tsx
  • apps/web/components/settings/account/api-tokens.tsx
  • apps/web/components/settings/account/security-settings.tsx
  • apps/web/components/settings/system/create-user-dialog.tsx
  • apps/web/components/settings/system/invite-dialog.tsx
  • apps/web/components/settings/system/users-table.tsx
  • apps/web/e2e/helpers/auth.ts
  • apps/web/e2e/playwright.config.ts
  • apps/web/e2e/tests/auth/auth-lifecycle.spec.ts
  • apps/web/e2e/tests/auth/auth-screenshots.spec.ts
  • apps/web/lib/api/client.ts
  • apps/web/lib/api/domains/auth-api.test.ts
  • apps/web/lib/api/domains/auth-api.ts
  • apps/web/lib/state/default-state.ts
  • apps/web/lib/state/slices/auth/auth-slice.test.ts
  • apps/web/lib/state/slices/auth/auth-slice.ts
  • apps/web/lib/state/slices/auth/types.ts
  • apps/web/lib/state/slices/features/features-slice.test.ts
  • apps/web/lib/state/slices/features/features-slice.ts
  • apps/web/lib/state/slices/features/types.ts
  • apps/web/lib/state/slices/index.ts
  • apps/web/lib/state/store-overrides.ts
  • apps/web/lib/state/store.ts
  • apps/web/src/auth-gate.tsx
  • apps/web/src/main.tsx
  • apps/web/src/settings-routes.tsx
  • apps/web/src/spa-routes.tsx
  • docs/decisions/2026-07-24-opt-in-authentication.md
  • docs/decisions/INDEX.md
  • docs/public/authentication.md
  • docs/public/coverage.json
  • docs/public/meta.json
  • docs/public/run-as-a-service.md
  • docs/specs/INDEX.md
  • docs/specs/auth/spec.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/opt-in-setup-authent-95z

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc3c8c4ea8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/backend/internal/auth/httpmw/middleware.go
Comment thread apps/backend/internal/gateway/websocket/office_notifications.go Outdated
…authent-95z

# Conflicts:
#	apps/backend/internal/backendapp/helpers.go
#	apps/backend/internal/backendapp/main.go
#	apps/web/lib/api/client.ts
#	apps/web/src/main.tsx
#	docs/decisions/INDEX.md
Comment thread apps/web/lib/state/store.ts Fixed
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @jcfs's task in 1s —— View job


I'll analyze this and get back to you.

…ffice run broadcasts

Address code-review findings on the opt-in authentication PR:

- P1: while auth is disabled the middleware injects a synthetic admin
  (RoleAdmin), and RequireAdmin alone let it through — so anyone hitting a
  not-yet-enabled instance could mint a PAT or plant an admin that survives
  enablement and hijacks first-run setup. Add authn.RequireRealIdentity and
  guard the credential/session/user-management routes with it (public
  bootstrap routes stay reachable). Responds 404 while disabled.
- P1: office run events carry task_id but no workspace_id, so they fell back
  to a global broadcast and leaked across users. Resolve the workspace from
  the task and route via the new Hub.BroadcastToWorkspaceOrDrop, which fails
  closed (drops rather than global-broadcasts) when the workspace is unknown
  and auth is enforced.
- nit: type createAuthSlice as a single-arg creator and call it with just set.

Also register the authentication docs page and its settings routes in
docs/public/coverage.json so the public-docs validator passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @jcfs's task in 1s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Pages docs preview

Open the docs preview

Built from docs commit 3301c57.

Stable PR alias: https://docs-pr-1930.landing-87j.pages.dev/docs

Resolve conflict in apps/backend/internal/system/system.go: keep the auth
branch's admin-gating on /updates/check and /updates/apply, and merge main's
new /updates/notification-settings routes — GET open to any authenticated user,
PUT admin-only since it writes a single install-wide preference.

Also realign mergeUIPanelState in default-state.ts to main's HydrationState
parameter type (the auth branch added this helper after main switched the merge
helpers from Partial<DefaultState> to HydrationState).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @jcfs's task in 0s —— View job


I'll analyze this and get back to you.

@jcfs
jcfs merged commit 919a39e into main Jul 24, 2026
60 checks passed
@jcfs
jcfs deleted the feature/opt-in-setup-authent-95z branch July 24, 2026 23:10
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