3.8 KiB
3.8 KiB
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)
- Role mechanism → better-auth
admin()plugin (defaultRole: 'worker',adminRoles: ['admin']). GivescreateUser/listUsers/setRoleserver+client APIs with access control for free (roadmap Decision #6 "don't hand-roll security"). - 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 viaauth.api.createUser(bypasses disableSignUp server-side). - 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 addsuser.role/banned/banReason/banExpires+session.impersonatedBy.admin/routes.mjscreateUser:if (!session && (ctx.request || ctx.headers)) throw UNAUTHORIZED→ callingauth.api.createUser({ body })with no headers skips the admin check ⇒ usable for seeding/tests.api/routes/sign-up.mjs:143: throwsBAD_REQUESTwhenemailAndPassword.disableSignUpis 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
Roleenum (worker | admin), optionaluser_name/user_emailonWorkSession(admin cross-user joins only), and anAdminUsercontract inpackages/shared/src/index.ts. - Task 2 — Test helpers. Centralized auth on
apps/api/test/helpers.ts(createTestUser/authToken/bearer/seedActivity) via server-sideauth.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']), setdisableSignUp: true, added the admin-plugin columns (user.role/banned/banReason/banExpires,session.impersonatedBy) and migration0002. New test asserts public sign-up is rejected. - Task 4 — Role-aware helper + activity lockdown.
getSessionUsernow returns role; addedisAdmin; extractedtoWorkSessionintoapps/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.tsexposes admin-onlyGET /api/admin/sessionsand/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 adminadmin@solelog.localviaauth.api.createUser, dev-only and idempotent. - Task 7 — Worker client. Removed self-signup: dropped
signUpfrom the API client andAuthContext, madeLoginlogin-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.