-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication
- Email + password, session-based via Redis
- Password storage: bcrypt hash
- Session: HttpOnly cookie, 7-day expiry
Public signup is bootstrap-only: it works exactly once, to create the first administrator on a fresh install. After the first user exists, POST /api/auth/signup returns 410 Gone and the /signup page renders a "Signup is closed" card. The "Create account" link on /login is also hidden once the system is bootstrapped (the frontend learns this from GET /api/config's bootstrapAvailable field).
- Validate: first name + last name required, email required, password 12–128 characters with at least 2 of {lowercase, uppercase, digit, symbol}
- Check email uniqueness
-
First user only: created as
admin,approved = true, auto-logged in. All later attempts are rejected with410.
After bootstrap, new users come in via admin invitation. The reset URL is always returned to the inviting admin so out-of-band sharing works even when SMTP is broken; when SMTP is enabled, the invitee also receives an email with the same link.
- An admin opens
/usersand clicks Invite user - The admin fills in email + role; the invitee's first/last name are NOT collected here
- The backend creates the user with a disabled bcrypt-hashed random password, empty placeholder names, and issues a one-time reset URL (60-minute TTL). Audit entry
user.invitedis written before the token is issued. - The response always includes the
resetUrl(the admin sees it in theResetUrlDialogwith a Copy button) and anemailed: booleanflag indicating whether theuser-inviteemail was actually dispatched. WithSMTP_ENABLED=true(default), the invitee receives the link; with SMTP off, only the admin sees it and shares it manually (chat, signal, in person, sealed envelope). - The invitee opens the URL and, on the same
/reset-passwordpage, fills in their first name, last name, and a new password. They can then log in with their assigned role.
Invited users skip the legacy "pending approval" state, they are approved = true from creation. The same ResetUrlDialog component powers both this flow and the admin password-reset flow, so the look-and-feel is identical.
The locale of the invite email follows the inviting admin's Accept-Language header, with MAIL_DEFAULT_LOCALE (en/fr) as the fallback.
- Look up user by email
- Verify password against bcrypt hash
- Create session in Redis (7-day expiry)
- Set HttpOnly cookie
- Approved users redirect to
/(dashboard), unapproved to/pending
The user-initiated reset flow depends on SMTP_ENABLED (default true). The frontend learns the current mode from smtpEnabled on GET /api/config.
SMTP enabled. /login shows a "Forgot password?" link. The /forgot-password page accepts an email; POST /api/auth/forgot-password issues a one-time reset token and emails the link (60-minute TTL). Successful password changes also trigger a confirmation email.
SMTP disabled. The public reset path closes:
- The "Forgot password?" link is hidden on
/login. -
/forgot-passwordrenders a "contact your administrator" notice (no form). -
POST /api/auth/forgot-passwordreturns410 Gonewith{ error: 'smtp_disabled' }. - No confirmation emails are sent on password changes.
In SMTP-disabled mode, recovery is admin-driven; see Production - Recovering a forgotten password for the admin-issued (/users -> Reset password) and CLI (pnpm --filter backend password:reset) paths.
Admins can reset another user's password from /users regardless of SMTP. Like invites, the response always includes resetUrl (shown to the admin) plus emailed: boolean. With SMTP enabled, the target user also receives an admin-password-reset email with the same link. With SMTP disabled, only the admin sees the URL and shares it out-of-band. Admins cannot use this endpoint on their own account.
The Prisma Role enum has five values. ADMIN / DPO / EDITOR are the primary write tiers; PROCESS_OWNER and AUDITOR are narrower functional roles, with AUDITOR serving as the default read-only tier.
| Capability | Admin | DPO | Editor | Process Owner | Auditor |
|---|---|---|---|---|---|
| View dashboard, register, checklist, recitals, violations | Yes | Yes | Yes | Yes | Yes |
| Create/edit treatments | Yes | Yes | Yes | Yes | - |
| Create/edit violations | Yes | Yes | Yes | - | - |
| Respond to checklist items | Yes | Yes | Yes | - | - |
| Validate/invalidate treatments | Yes | Yes | - | - | - |
| Delete treatments | Yes | Yes | - | - | - |
| Export treatments as CSV | Yes | Yes | - | - | Yes |
| View audit log | Yes | Yes | - | - | Yes |
| Access DSR records (read/write) | Yes | Yes | - | - | - |
| Manage users (approve, change role, deactivate) | Yes | - | - | - | - |
| Edit organization settings | Yes | - | - | - | - |
PROCESS_OWNER : scoped to treatment create/update only. Cannot validate, cannot touch violations or other modules.
AUDITOR : read + export only. Never writes. Intended for third-party compliance reviews.
Self-management restriction: admins cannot change their own role, deactivate themselves, or admin-reset their own password. The corresponding endpoints return 403 Forbidden. This is enforced server-side regardless of UI state. To recover the sole admin's password without another admin available, see Production - Recovering a forgotten password.