Authentication & Roles
JWT-based auth with four built-in roles and a granular permission registry

Overview

Domma CMS uses JWT-based authentication for all admin operations. The role-and-permission system allows granular access control across 11 resource types. Authentication is handled server-side by Fastify routes, with tokens stored in localStorage on the client.

JWT Tokens
Short-lived access tokens + long-lived refresh tokens
4 Built-in Roles
Admin, Manager, Editor, Subscriber — plus custom roles
44 Permission Flags
11 resources × 4 actions (create, read, update, delete)

Login Flow

The login sequence follows a standard JWT handshake with automatic silent refresh.

  1. User visits /admin — redirected to login if not authenticated
  2. Enter email and password — POST /api/auth/login
  3. Server validates credentials (bcrypt compare) — returns { accessToken, refreshToken }
  4. Access token stored in localStorage; refresh token stored in an httpOnly cookie
  5. Subsequent requests include Authorization: Bearer {token} header
  6. Access token expires in 15 minutes; auto-refreshed using refresh token (7 days)
Silent refresh — The CMS client intercepts 401 responses, attempts a token refresh, then retries the original request transparently. Users are only prompted to log in again when the refresh token itself expires.

Registration (First-Run Setup)

Registration is intentionally restricted to prevent unauthorised account creation on production sites.

  • Registration is disabled by default after initial setup
  • First admin account created via npm run setup interactive wizard
  • Additional users created by admins: Admin → Users → New User
  • Optional: enable self-registration in config (auth.allowRegistration: true)
  • When enabled, users self-register at /register with email verification (if SMTP is configured)
  • New self-registered users are assigned the subscriber role by default
Production note — Keep auth.allowRegistration set to false unless you specifically need a self-service sign-up flow. Enabling it on a public site without email verification exposes the admin area to spam accounts.

Forgot / Reset Password

Password resets use a time-limited, single-use token delivered by email. SMTP must be configured in site settings for this flow to work.

  1. User clicks "Forgot password" on the login page
  2. Enter email address — POST /api/auth/forgot-password
  3. If the email exists, the server sends a reset email with a time-limited token (1 hour)
  4. User clicks the link — token is validated — user sets a new password
  5. All existing sessions are invalidated immediately on password reset
  6. Requires SMTP configured under Admin → Settings → Email
The server always returns a success response to the forgot-password endpoint regardless of whether the email exists, to prevent user enumeration attacks.

JWT Tokens

Domma CMS uses a two-token system to balance security (short-lived access) with usability (transparent refresh).

Access Token
  • Short-lived — 15 minutes
  • Carries: userId, email, role, iat, exp
  • Stored in localStorage
  • Sent with every API request as a Bearer token
Refresh Token
  • Long-lived — 7 days
  • Stored as an httpOnly cookie (not accessible via JS)
  • Used only to obtain new access tokens silently
  • Rotated on each use

The JWT_SECRET is generated automatically during the npm run setup wizard and stored in your .env file. Rotate it by regenerating and restarting — all active sessions will be invalidated.

Token payload example: { "userId": "abc123", "email": "admin@example.com", "role": "admin", "iat": 1700000000, "exp": 1700000900 }

Four Built-in Roles

Built-in roles cover the most common team structures. Each role is cumulative — higher roles include all permissions of roles below them.

Role Slug Description
admin admin Full access to everything: all content, all users, all settings, and all admin features. Cannot be deleted.
manager manager Manage all content (pages, collections, forms, media, blocks) but not user management or system settings.
editor editor Create and edit content (pages, media) but not delete or manage collections/forms.
subscriber subscriber Read-only access to the admin dashboard; primarily used for members-only content on the public site.
Built-in roles cannot be renamed or deleted, but you can create unlimited custom roles (see next section) to model your exact team structure.

Custom Roles

Custom roles let you define exactly the right access level for your team without over-provisioning permissions.

Creating a Custom Role

  1. Navigate to Admin → Roles → New Role
  2. Enter a role name and slug (e.g. Content Reviewer / content-reviewer)
  3. Configure permissions per resource (11 resources × 4 actions = 44 flags)
  4. Save the role

Assigning a Custom Role

  1. Navigate to Admin → Users
  2. Select the user to update
  3. Open the Edit User panel
  4. Change the Role dropdown to your custom role
  5. Save — changes take effect on the user's next request
Custom roles inherit no permissions by default — you must explicitly opt in to each resource + action pair. This prevents accidental over-permission when creating new roles.

Permission Registry (11 Resources × 4 Actions)

Every admin action is governed by a permission flag. The table below shows the default grants for each built-in role.

Resource Action admin manager editor subscriber
pagescreate
read
update
delete
mediacreate
read
update
delete
collectionscreate
read
update
delete
formscreate
read
update
delete
blockscreate
read
update
delete
views Procreate
read
update
delete
actions Procreate
read
update
delete
settingscreate
read
update
delete
navigationcreate
read
update
delete
pluginscreate
read
update
delete
userscreate
read
update
delete

Pro resources (views, actions) are available on Pro and Enterprise plans. Custom roles can be configured with any combination of the above 44 flags.

User Profiles

Every authenticated user has an extended profile beyond their core auth fields. Profiles are stored as a preset collection linked to the auth user record by userId.

Profile Fields

  • displayName — public-facing name
  • bio — short biography (textarea)
  • avatar — media picker (linked to Media Library)
  • website — personal or professional URL
  • socialLinks — object of platform → URL pairs
  • location — city / country free text

Accessing Profiles

  • Admin UI: Users → [User] → Profile tab
  • API: GET /api/users/{id}/profile
  • Templates: {{user.profile.*}} on auth-gated pages
  • Users can edit their own profile under Account Settings
  • Admins can edit any user's profile
Profile data is only exposed in page templates when the page has requiresAuth: true configured. On public pages, {{user.profile.*}} variables resolve to empty strings.

API Authentication

Use Domma CMS as a headless CMS by authenticating programmatically and including the access token with every request.

Login and obtain tokens

POST /api/auth/login
Content-Type: application/json

{
  "email": "admin@example.com",
  "password": "your-password"
}

// Response
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Use the access token in subsequent requests

GET /api/collections/posts/entries
Authorization: Bearer {accessToken}

Refresh the access token

POST /api/auth/refresh
Cookie: refreshToken={token}

// Response
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Security — Never expose your access token in client-side code that is publicly readable. For server-to-server integrations, consider creating a dedicated service account with the minimum required permissions.