63 lines
3.8 KiB
Markdown
63 lines
3.8 KiB
Markdown
# Session: 2026-06-17 — Phase 2 (Accounts & roles)
|
|
|
|
## Goal
|
|
|
|
Add worker/admin roles, admin-creates-users, and role-based data scoping to the backend.
|
|
Backend-only this phase; the React admin panel stays Phase 3.
|
|
|
|
## Decisions (confirmed by maintainer this session)
|
|
|
|
1. **Role mechanism** → better-auth `admin()` plugin (`defaultRole: 'worker'`, `adminRoles: ['admin']`).
|
|
Gives `createUser` / `listUsers` / `setRole` server+client APIs with access control for free
|
|
(roadmap Decision #6 "don't hand-roll security").
|
|
2. **Public sign-up** → **closed** (`emailAndPassword.disableSignUp: true`). Admin creates users.
|
|
Worker client becomes login-only (Registreren toggle removed). Dev seed still creates a dev
|
|
worker **and** a dev admin via `auth.api.createUser` (bypasses disableSignUp server-side).
|
|
3. **Phase 2 client scope** → backend-only. Admin UI is Phase 3. Only worker-client change: drop self-signup.
|
|
|
|
## Key better-auth facts established (read from installed v1.6.18 source)
|
|
|
|
- `admin/schema.mjs`: plugin adds `user.role/banned/banReason/banExpires` + `session.impersonatedBy`.
|
|
- `admin/routes.mjs` `createUser`: `if (!session && (ctx.request || ctx.headers)) throw UNAUTHORIZED`
|
|
→ calling `auth.api.createUser({ body })` with **no headers** skips the admin check ⇒ usable for seeding/tests.
|
|
- `api/routes/sign-up.mjs:143`: throws `BAD_REQUEST` when `emailAndPassword.disableSignUp` is set
|
|
(sign-in unaffected). createUser is a separate endpoint, so it still works.
|
|
- Admin endpoints auto-mount under `/api/auth/admin/*` via the existing `/api/auth/*` handler.
|
|
|
|
## Work done
|
|
|
|
Implemented Phase 2 task-by-task per `docs/plans/phase-2-accounts-roles.md` (TDD throughout):
|
|
|
|
- **Task 1 — Shared contracts.** Added `Role` enum (`worker | admin`), optional `user_name` /
|
|
`user_email` on `WorkSession` (admin cross-user joins only), and an `AdminUser` contract in
|
|
`packages/shared/src/index.ts`.
|
|
- **Task 2 — Test helpers.** Centralized auth on `apps/api/test/helpers.ts`
|
|
(`createTestUser` / `authToken` / `bearer` / `seedActivity`) via server-side `auth.api.createUser`,
|
|
removing every test's dependency on the public sign-up route so it can be closed.
|
|
- **Task 3 — Admin plugin + close sign-up.** Wired better-auth's `admin()` plugin
|
|
(`defaultRole: 'worker'`, `adminRoles: ['admin']`), set `disableSignUp: true`, added the
|
|
admin-plugin columns (`user.role/banned/banReason/banExpires`, `session.impersonatedBy`) and
|
|
migration `0002`. New test asserts public sign-up is rejected.
|
|
- **Task 4 — Role-aware helper + activity lockdown.** `getSessionUser` now returns role;
|
|
added `isAdmin`; extracted `toWorkSession` into `apps/api/src/lib/work-session.ts`; gated
|
|
activity POST/PUT/DELETE to admins (GET stays open to any authenticated user).
|
|
- **Task 5 — Admin router.** `apps/api/src/routes/admin.ts` exposes admin-only
|
|
`GET /api/admin/sessions` and `/api/admin/sessions/active` (all users, joined with activity +
|
|
user name/email); 401 unauthenticated, 403 non-admin.
|
|
- **Task 6 — Dev seed.** Seeds dev worker `worker@solelog.local` + dev admin
|
|
`admin@solelog.local` via `auth.api.createUser`, dev-only and idempotent.
|
|
- **Task 7 — Worker client.** Removed self-signup: dropped `signUp` from the API client and
|
|
`AuthContext`, made `Login` login-only (no Registreren toggle).
|
|
- **Task 8 — Docs, lint, verification.** Updated this log, the roadmap status, and the worker
|
|
README (both dev logins; self-registration closed). Ran lint/format/typecheck and both test
|
|
suites green, plus the live HTTP smoke test proving the role rules.
|
|
|
|
## Plane
|
|
|
|
- Epic + tasks created under SoleLog (SL). See plan doc for the mapping.
|
|
|
|
## Next
|
|
|
|
Run the build (workflow), verify live (admin can manage users + see all sessions; worker cannot;
|
|
sign-up closed), then update roadmap status to Phase 2 = Done.
|