b20047b3eb
* feat(plugins): add auth capability for plugin-provided OIDC/SAML login Plugins can now authenticate a visitor against an external IdP (OIDC/SAML) and contribute a login button to the pre-auth screen: - new `auth` manifest capability + a capability-gated X-Kandev-Auth-Login webhook-response directive; the host mints the kandev_session (ADR 0050), the plugin never receives the raw token. Plugin-set Set-Cookie is dropped. - auth.Service.AuthenticateExternal: existing-identity / link-by-email / JIT member provisioning; refuses to auto-link to an admin; verified-email is the plugin's contract. - auth_providers manifest block + plugins.SSOProviders() populate the anonymous boot payload (auth.ssoProviders); the login page renders a "Continue with <provider>" button per provider. - ADR 0050 + plugin-manifest / plugin-authoring / auth spec docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: exclude publish_review_findings_kandev from public-docs coverage The native code-review agent (#1957) added the publish_review_findings_kandev MCP tool but did not update docs/public/coverage.json; that PR did not touch docs/public so the path-filtered validator never ran. It's an internal review reporting tool, not a user-facing feature, so record it as an exclusion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(plugins,auth): address review on SSO directive and JIT provisioning - takeAuthLoginDirective strips every case variant of X-Kandev-Auth-Login and rejects (403) when a plugin sends more than one, so no variant leaks to the browser and login never uses a nondeterministically-picked assertion. - provisionExternalUser rolls back the just-created user when the identity insert fails (transient error or a lost UNIQUE(provider,subject) race) and resolves to the winning identity, so no account is left without a login and no email is reserved by an unusable row. Adds userstore.DeleteUser. - auth spec: mark the login-page SSO buttons + provider discovery as landed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: drop redundant coverage exclusion; main now covers publish_review_findings_kandev Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
107 lines
5.9 KiB
Markdown
107 lines
5.9 KiB
Markdown
---
|
|
status: building
|
|
created: 2026-07-24
|
|
owner: tbd
|
|
---
|
|
|
|
# Opt-in Authentication & Multi-User Segregation
|
|
|
|
## Why
|
|
|
|
Kandev ships as a single-user local tool with zero authentication: the backend
|
|
binds `0.0.0.0` by default and anyone who can reach the port owns the instance
|
|
and every agent credential on it. Teams who share a Kandev server (VPS, homelab,
|
|
office box) need user accounts and privacy between users — without burdening
|
|
the local single-user install with a login screen it never asked for.
|
|
|
|
## What
|
|
|
|
- **Opt-in.** Authentication is OFF by default. A disabled instance behaves
|
|
byte-identically to pre-auth Kandev — no login, no auth UI.
|
|
- **Enablement is a runtime feature toggle.** Authentication is turned on/off
|
|
through the `features.auth` runtime flag ("Authentication & users" in
|
|
`Settings > System > Feature Toggles`, or `KANDEV_FEATURES_AUTH=true`) — a
|
|
restart-required flag like the other feature toggles, with no separate
|
|
Authentication settings page. Turning it on (and restarting) enters **setup
|
|
mode**: the first visitor completes a wizard (email + password) and becomes
|
|
the admin. The wizard promotes the existing single-user profile — settings,
|
|
workspaces, and secrets carry over to the admin account. The effective mode
|
|
is derived: `flag off → disabled`, `flag on + no admin → setup`,
|
|
`flag on + admin exists → enabled`.
|
|
- **Accounts.** Local email + password (argon2id). The identity schema is
|
|
provider-based so OIDC/SSO can be added later without migration. Roles:
|
|
`admin` (user management, auth settings, destructive system operations,
|
|
feature toggles) and `member`. Users can be disabled (revokes all sessions
|
|
and tokens); the last active admin cannot be demoted or disabled.
|
|
- **Sessions & tokens.** Browser sessions are opaque cookies
|
|
(`kandev_session`, HttpOnly, SameSite=Lax, sliding 30-day expiry,
|
|
DB-backed and revocable from `Settings > Account`). Programmatic clients
|
|
(external MCP, scripts, CI) use personal access tokens
|
|
(`kandev_pat_…`, shown once, revocable).
|
|
- **Invites.** Admins mint tokenized invite URLs (`/invite?token=…`,
|
|
optional pinned email, member/admin role, 7-day default expiry, single
|
|
use). No email server required. Admins can also create accounts directly.
|
|
- **Segregation: per-user workspaces.** Every workspace has one owner. Users
|
|
see and touch only their own workspaces — tasks, sessions, repositories,
|
|
workflows, plans, walkthroughs, terminals, VS Code, port previews, git
|
|
snapshots, session turns/context, secrets, **third-party integration
|
|
settings (GitHub/GitLab/Jira/Linear/Sentry/Slack/Azure), automations**, and
|
|
WebSocket events included. Cross-user access returns 404 — knowing an ID
|
|
from another user's workspace does not grant access. Admins do NOT see other
|
|
users' workspaces (hard privacy; admin is a management role, not a
|
|
visibility role). Pre-auth data is claimed by the admin at setup.
|
|
- **Shared surfaces (instance-global by design).** Executors, executor
|
|
profiles, environment definitions, agent profiles/agents, editors, prompts,
|
|
sprites, voice, notification providers, and system pages remain shared
|
|
across the instance; mutation of system settings and feature toggles is
|
|
admin-only when auth is enabled. **Note:** a shared agent profile means all
|
|
users share the agent's provider credentials — see the OS-level limit below.
|
|
- **Public endpoints.** `/health`, the SPA shell/static assets, the boot
|
|
payload (`/api/v1/app-state` — returns only `{features, auth}` for
|
|
anonymous visitors), `/api/v1/features`, credential endpoints
|
|
(login/setup/invite-accept), and self-authenticating webhooks (automation
|
|
`X-Webhook-Secret`, office channel HMAC, plugin webhooks).
|
|
- **Disable.** An admin can turn auth off again (unless env-forced).
|
|
Ownership data is retained; everyone reaching the instance has full access
|
|
again.
|
|
|
|
## API surface
|
|
|
|
`/api/v1/auth/*`: `setup`, `login`, `logout`, `me`, `password`, `sessions`,
|
|
`tokens`, `invites` (+ `preview`, `accept`), `settings`. `/api/v1/users`
|
|
(admin CRUD: list, create, role/status). WS: cookie on same-origin upgrade or
|
|
`?token=<PAT>`; subscriptions and RPC actions are scoped to the caller.
|
|
|
|
## Failure modes
|
|
|
|
- Wrong credentials and unknown emails return the same generic 401; login is
|
|
rate-limited (10 attempts / 5 min per IP+email).
|
|
- Foreign workspaces/tasks read as 404 — existence is not leaked.
|
|
- A server bound to non-loopback interfaces with auth disabled logs a
|
|
prominent startup warning.
|
|
- Sessions/PATs of disabled users fail closed immediately.
|
|
|
|
## Known v1 limits
|
|
|
|
- **Filesystem / agent-credential isolation is NOT enforced.** Worktrees and
|
|
repos live under one `~/.kandev` tree readable by the OS user running the
|
|
backend, and agent CLI logins (`gh auth`, `claude login`, provider API keys)
|
|
authenticate as that OS user — so all app-users share the same on-disk agent
|
|
credentials. Application-layer isolation (DB + the checks above) is the
|
|
boundary between users' *kandev data*; it does NOT sandbox the filesystem or
|
|
per-user agent auth. For hard isolation of agent credentials, run separate
|
|
kandev instances per user or use per-user OS accounts / sandboxed executors
|
|
(future work).
|
|
- No workspace sharing/membership — one owner per workspace.
|
|
- No first-party OIDC/SSO. The host supports **plugin-provided** external login
|
|
(an `auth`-capable plugin asserts a validated OIDC/SAML identity and the host
|
|
mints the session — see ADR 0050); the pre-auth login-page SSO buttons and
|
|
anonymous provider discovery (`auth.ssoProviders` in the boot payload) ship
|
|
here too. What remains is a published IdP plugin (e.g. Google OIDC, developed
|
|
in its own repo) and setup-mode first-admin bootstrap via SSO (SSO requires
|
|
enforced auth today).
|
|
- Office workspace-scoped HTTP routes (those carrying a `:wsId`) are
|
|
ownership-checked when auth is enabled; office run *subscriptions* are not
|
|
yet ownership-checked (run events carry no workspace context at the
|
|
subscription layer).
|