Authentication
Sign up and sign in
| Endpoint | Purpose |
|---|---|
POST /api/auth/register | Individual user — name, email, password |
POST /api/auth/register-company | Company — creates the organization, its first ROLE_ORG_ADMIN, and a trial plan |
POST /api/auth/login | Email + password → JWT |
Passwords are hashed with BCrypt. On login the backend returns a JWT (jjwt, HMAC); the frontend stores it in localStorage and sends it as Authorization: Bearer <token> on every request. JwtAuthenticationFilter validates the signature, loads the user, and puts them on the security context. Everything under /api/auth/** and /api/public/** is open; everything else needs a valid token.
Roles
| Role | Portal | Scope |
|---|---|---|
ROLE_USER | User dashboard | Own files, signing documents, and quota |
ROLE_ORG_ADMIN | Org dashboard | The whole organization — members, roles, plan, limits (ROLE_ADMIN is a legacy alias) |
ROLE_PLATFORM_OWNER | Platform dashboard | Every user and org platform-wide |
The /dashboard route renders a different component per role, decided by AuthContext helpers (isPlatformOwner, isOrgAdmin).
Platform admins are a separate table
Platform operators are not User rows. They live in admin_accounts — their own model, their own login (POST /api/admin/auth/*), authenticating as ROLE_PLATFORM_OWNER. Creating the first one requires ADMIN_SETUP_KEY; after that, existing admins manage the rest from the console. This keeps platform credentials off the same surface tenants authenticate against.
Team invitations
An org admin adds members from Team (POST /api/org/team/invite) — no open sign-up into an existing org. Members can be given a role (PUT /api/org/team/{userId}/role) and deactivated / reactivated. GET /api/org/me returns the caller's org; GET /api/org/team lists members.
Recipients don't authenticate
Someone invited to sign a document isn't a user. They open a one-time link carrying a UUID token, and — if the document owner enabled it — confirm an email OTP before signing. See E-signature.