Implements Phase 3 of the auth roadmap: multiple user accounts (cap 10),
an admin/member role model, and admin-only gating of config-mutating
routes. Dashboard data stays shared across all users (per the product
decision in HANDOFF.md — this is a household/self-hosted dashboard, not
a multi-tenant app), so there is no per-user data isolation.
Schema (backend/src/db/index.ts):
- Idempotent migration adds `role` (default 'admin') and `active`
(default 1) columns to `users` when missing. The 'admin' default means
the pre-existing single user is backfilled to admin on deploy and keeps
full access; newly created users are inserted explicitly as 'member'.
Verified against a production-like old schema (columns added, existing
user backfilled to admin/active).
Auth + access control:
- `/api/setup` creates the first user as admin. Login enforces `active`
(deactivated accounts get 403) and embeds the live role in the session.
- `app.authenticate` now reads role+active fresh from the DB on every
request (not from the possibly-stale JWT claim), rejects inactive
accounts, and stashes the role on req.user.
- New `requireAdmin` (auth + role check) and `adminOnly` (role check for
routes already behind the plugin-level authenticate hook) decorators.
User management (admin-only, in auth.ts):
- GET/POST/PUT/DELETE /api/users — list, create (admin sets a temp
password; no public signup), change role, activate/deactivate, delete.
- 10-user cap enforced server-side; guard rails prevent removing the last
active admin (demote/deactivate/delete) and deleting your own account;
deactivating or deleting a user drops their sessions immediately.
Admin-only route gating (members get 403):
- integrations create/update/delete/test, tunnels create/delete, data
export/import. Read routes and tunnel connect/disconnect stay open to
all authenticated users, as do all the SSH/Docker/RDP tools and
bookmarks (members are trusted to use the tooling, per product decision).
Frontend:
- api.ts: listUsers/createUser/updateUser/deleteUser + ManagedUser type;
role+active added to AuthUser.
- Settings: new admin-only "Users" section (create form, role toggle,
activate/deactivate, delete, 10-cap indicator). Nav filters the Users
tab by role and guards ?tab= deep-links. Data & Backup shows an
admin-only notice for members; Integrations shows a read-only banner
for members. (Backend remains the real enforcement boundary.)
Verified end-to-end against a throwaway backend: role assignment,
member 403s on every admin-only route + 200s on shared/read routes,
admin 200/201s, last-admin guards (409/400), deactivation killing an
active session and blocking re-login (then reactivation restoring it),
and the 10-user cap (409 on the 11th). Both frontend and backend
type-check clean.
Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>