Skip to main content

Roles & Permissions (RBAC)

Role-based access control (RBAC) is the classical authorization model: what you may do follows from the roles you hold. A permission names a single action on a resource (for example reports.view). A role is a named bag of permissions. Users are assigned roles, and a user's effective permissions are the union of permissions from every role they hold, directly or through a group.

Use RBAC when access is mostly a function of job title — "admins can manage users, editors can edit content" — and the set of permissions is small enough to enumerate. When decisions depend on per-resource ownership, context, or relationships, combine RBAC with ABAC (attribute-based access control) or Zanzibar.


Concepts​

Permissions​

A permission represents a specific action on a resource.

Example PermissionDescription
reports.viewView reports
reports.editCreate and edit reports
reports.deleteDelete reports
invoices.manageFull control over invoices
tickets.viewView support tickets
tickets.assignAssign tickets to people

Custom permission slugs use lowercase letters, digits, dots, hyphens and underscores (reports.view); a colon is refused. The built-in catalogue that guards the portal and Admin API uses the same resource.action shape (users.create, roles.edit, settings.manage, jit.approve, and so on), and its namespaces are reserved (see Reserved permission slugs), so pick resource names from your own application. Both kinds live in the same table and can be attached to roles.

Roles​

A role is a named collection of permissions.

RolePermissions
viewerreports.view, tickets.view
editorreports.view, reports.edit, invoices.view
adminAll permissions
auditortickets.view, reports.view

Role Assignment​

Users can hold one or more roles. Effective permissions are the union across all assigned roles.


Managing Permissions​

Create a Permission​

  1. In the sidebar, under Access control, open Roles & permissions and switch to the Permissions tab (/orgs/{orgId}/portal/permissions). It has two sub-tabs, Custom permissions (the ones you define) and System permissions (the built-in catalogue), each with a count and a search box that matches slug, name or description. Permissions are grouped by resource (for example Tickets or Invoices); click a group to list its permissions, each with its slug, display name and a row menu.

The Permissions tab with custom permissions grouped by resource, the first group expanded

  1. Click Create custom permission.
  2. Fill in:
FieldDescriptionExample
ResourceThe kind of thing being protectedreport
ActionWhat may be done to itview
SlugDerived from the resource and action as resource.action; this is the string your code checks and it cannot be changed laterreport.view
Display nameRequired. Human-readable name shown in the role editor and the permission matrixView reports
DescriptionOptional. What this permission grantsOpen and read reports
  1. Click Create Permission. A new permission is held by nobody until you add it to a role.

The Create custom permission form with resource, action and the derived slug

Via API​

curl -X POST https://your-domain.com/orgs/{orgId}/api/v1/admin/permissions \
-H "Authorization: Bearer {admin_token}" \
-H "Content-Type: application/json" \
-d '{
"name": "View reports",
"slug": "reports.view",
"description": "View reports and their details"
}'

Managing Roles​

Create a Role​

  1. In the sidebar, under Access control, open Roles & permissions (/orgs/{orgId}/portal/roles). The Roles tab lists every role with its description, number of permissions and type (Custom or System), and has a search box for the name or slug. The row menu (...) edits or deletes a custom role; system roles only have View.

The Roles tab listing custom roles and the system Tenant Admin role, with the Create role button

  1. Click Create role. When an AI provider is configured, Draft with AI next to it opens AI policy authoring.
  2. Fill in:
FieldDescriptionExample
Role nameDisplay name. The slug is derived from it and shown under the fieldEditor (slug editor)
DescriptionRole purposeCan view and edit users and applications
  1. Under Permissions, set an access level (None, View, Edit or Full) per area, or open an area to tick individual permissions. The Start from presets (Read-only, Editor, Administrator) fill in a starting set.
  2. Click Create role. The Role summary beside the form lists the selected areas and their levels as you go, and a role name that is already taken is refused.

The Create role form with the role name, description, role summary and permission areas

To change a role later, open it from the Roles list, adjust its permissions and click Save changes. System roles can only be viewed.

A custom role opened for editing, with its details, role summary and permission picker

Via API​

# Create a role
curl -X POST https://your-domain.com/orgs/{orgId}/api/v1/admin/roles \
-H "Authorization: Bearer {admin_token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Editor",
"slug": "editor",
"description": "Can view and edit reports and invoices"
}'

# Set a role's permissions (PUT replaces the full set; values are permission slugs)
curl -X PUT https://your-domain.com/orgs/{orgId}/api/v1/admin/roles/{roleId}/permissions \
-H "Authorization: Bearer {admin_token}" \
-H "Content-Type: application/json" \
-d '{
"permissions": ["reports.view", "reports.edit", "invoices.view"]
}'
tip

Use PUT .../permissions to replace the role's full permission set with slug values. To add one or more permissions without removing existing ones, POST .../permissions instead with permission IDs: {"permissionId": 12} or {"permissionIds": [12, 13]}.


Assigning Roles to Users​

Via Portal​

  1. Go to Users (/orgs/{orgId}/portal/users).
  2. Click the user's name to open the user.
  3. Open the Roles tab.
  4. Tick or untick roles (the search box filters them), then click Save changes. The Effective access panel beside the list shows how many permissions the user ends up with, split into direct grants and permissions from roles.

The Roles tab of a user, with a checkbox per role and the Effective access panel

Via API​

# Set a user's roles (PUT replaces the full set; values are role slugs)
curl -X PUT https://your-domain.com/orgs/{orgId}/api/v1/admin/users/{userId}/roles \
-H "Authorization: Bearer {admin_token}" \
-H "Content-Type: application/json" \
-d '{
"roles": ["editor", "auditor"]
}'

# Add a single role without removing existing ones
curl -X POST https://your-domain.com/orgs/{orgId}/api/v1/admin/users/{userId}/roles \
-H "Authorization: Bearer {admin_token}" \
-H "Content-Type: application/json" \
-d '{"roleSlug": "editor"}'

# List a user's roles
curl https://your-domain.com/orgs/{orgId}/api/v1/admin/users/{userId}/roles \
-H "Authorization: Bearer {admin_token}"

Roles in Tokens​

When a user authenticates, their roles can be included as claims in the access token. The access token is a JWT (JSON Web Token: a signed JSON payload the client presents on each request).

Roles are opt-in per application: the roles claim (in the access token and the UserInfo response) is sent only when User roles is ticked and saved under the application's Security → JWT claims (JWT access tokens). A client that never enabled it, including dynamically registered clients and clients identified by a metadata document (CIMD), gets no role names. See Claims and roles.

{
"sub": "user-uuid",
"organization": "acme-corp",
"roles": ["editor", "auditor"],
"iat": 1706400000,
"exp": 1706403600
}

The token carries role names only, never the expanded permission list. Your application can verify the token signature and map role names to its own checks, or ask LumoAuth for a permission decision at request time (below).


Checking Permissions​

In Your Application​

Verify the access token and read its roles claim (when the application has User roles enabled), then decide locally which roles may do what.

// Node.js example
const token = jwt.verify(accessToken, publicKey);

if ((token.roles || []).includes('editor')) {
// Allow report edit
} else {
// Deny access
}

Via the Permission Check API​

To decide on individual permissions rather than role names, ask LumoAuth at request time. These endpoints evaluate against the caller's own access token (send it as the Bearer token), so they need no admin privileges.

# Check a single permission for the current user
curl -X POST https://your-domain.com/api/v1/permissions/check \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"permission": "reports.edit"}'
# -> {"allowed": true, "permission": "reports.edit", "context": {}}

# Check several at once
curl -X POST https://your-domain.com/api/v1/permissions/check-bulk \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"permissions": ["reports.view", "reports.edit", "reports.delete"]}'

# List the current user's effective permissions and roles
curl https://your-domain.com/api/v1/permissions/user \
-H "Authorization: Bearer {access_token}"

Via Permission Tester​

Use Test access at /orgs/{orgId}/portal/permissions/tester (Access control → Test access, Roles & permissions tab) to check whether a user holds a permission. Pick a User (it starts on you) and a Permission, optionally add context as JSON, and click Run test; the Quick presets run common checks in one click. The result shows whether access is granted and, under Where it comes from, which role, group or direct grant provides it.

Test access showing an Allowed result and the role that granted it


How the admin permissions work​

The organization portal and the Admin API are governed by the built-in resource.action catalogue. There is no single "admin" switch:

  • Getting into the portal. Anyone holding at least one admin-plane system permission (any catalogue permission except the platform-only tenants.*) can open the portal. Holding jit.approve alone, for example, is enough to reach the approval inbox.
  • Every page checks its own permission. Opening Users needs users.view, saving a user needs users.edit, and so on. A page you cannot use answers 403, and the sidebar hides entries you hold no permission for.
  • Every Admin API endpoint declares its permission. The per-endpoint list is under Required permissions. API keys are additionally limited to what their creator holds.
  • settings.manage is not "full admin". It covers authentication and security policy (sign-in, MFA (multi-factor authentication) and session settings, the organization profile over the API, audit-log retention), API keys, custom domains, directory (LDAP) and HR connections, log shippers, alerting and the other integrations. It does not grant users, roles, clients, webhooks, agents or anything else; give those permissions explicitly.

Permissions added in the 2026-09-25 release, seeded for every organization and granted to the built-in Tenant Admin role:

SlugGrants
abac.viewView ABAC attribute definitions and policies
abac.manageCreate, edit and delete ABAC attributes and policies, and set user and resource attribute values
gdpr.manageData subject requests, consent, retention, breaches, and exports of other users' personal data
gdpr.dpoData Protection Officer duties: DPIAs, breach sign-off, supervisory-authority reporting
billing.manageView and change the subscription plan, add-ons and payment details
jit.approveApprove or deny AI-agent just-in-time requests (now seeded for every organization)
authz.checkAsk authorization questions about other users and agents (now seeded for every organization)

Protecting stronger accounts​

An administrator can only take account-changing actions on users who hold no more than they do. Setting a password, changing the e-mail, activating or deactivating, blocking, deleting, removing an authenticator and issuing a temporary access code all require that you hold every permission the target user holds. The same rule applies in the portal, the Admin API and to API keys (checked against the key's creator).

Reserved permission slugs​

Custom permissions cannot take a system slug (users.view, settings.manage, …), any slug inside a system namespace (for example users.impersonate or billing.refund), a tenants. slug, or one of the retired slugs the platform once checked (agents.manage, users.manage, zanzibar.manage, webhooks.test, gdpr.export_other, abac.policies.*, abac.attributes.*). This applies to the portal, the Admin API and AI policy authoring, which also only attaches permissions the author already holds.


No privilege gain​

One rule decides every place a role can be handed out: nobody can grant more than they hold, and no automated path can create an administrator. The rule is enforced in the same way whether the assignment is made by a person or by an integration, so there is no back door to check for.

Where roles are assignedWhat is enforced
User editor and PUT/POST .../users/{userId}/rolesThe acting administrator must hold every permission in the role. System roles are assignable only by administrators who already hold them.
Role editor and .../roles/{roleId}/permissionsAn administrator cannot attach a permission they do not hold themselves.
GroupsAdding a role to a group, or a user to a group, is checked against the acting administrator in the same way.
SCIM (automatic user provisioning) /Groups membershipAn IdP can add users to job-function roles, never to a system role or a role that confers administrator authority. Refused with 403 mutability. See SCIM Groups.
Social-login default rolesA provider's default roles cannot be system or administrator-conferring roles, and the administrator saving them must hold every permission in them. See Social Login.
SAML JIT provisioningAny privileged role found in an IdP's stored default-role list is skipped at sign-in and an audit event is written.

A role confers administrator authority when it holds any permission that changes who can do what in the organization:

FamilyPermissions
Organization settingssettings.create, settings.edit, settings.delete, settings.manage
Users, roles, groupsusers.create, users.edit, users.delete, roles.create, roles.edit, roles.delete, roles.assign-permissions, groups.create, groups.edit, groups.delete
Customer organizationsorganizations.members.manage, organizations.invitations.manage
Identity providers and provisioningidps.create, idps.edit, idps.delete, scim.create, scim.edit, scim.delete, scim.manage
Signing keyssigning_keys.create, signing_keys.edit, signing_keys.rotate, signing_keys.revoke, signing_keys.delete
OAuth clientsclients.create, clients.edit, clients.delete, clients.rotate-secret
AI agents, policies, approvalsagents.create, agents.edit, agents.delete, policies.create, policies.edit, policies.delete, jit.approve
Fine-grained authorizationzanzibar.create, zanzibar.edit, zanzibar.delete, abac.manage
Privacy and billinggdpr.manage, gdpr.dpo, billing.manage

Read-only permissions (*.view, audit.*, analytics.*, webhooks.*, branding.*, abac.view) and your own custom application permissions never make a role administrative, so automated paths can hand them out freely.

Why this matters: an administrator who can only edit identity providers (idps.edit) could otherwise register a social provider whose default role is Tenant Admin, sign in through it with a fresh account, and become an administrator. With the rule applied everywhere, the only way to become an administrator is to be made one by someone who already is.


Best Practices​

PracticeDescription
Principle of least privilegeAssign the minimum permissions needed
Assign permissions to roles, not usersKeeps user management simple
Consistent namingFollow the resource.action convention
Avoid catch-all rolesPrefer focused roles over a single omnibus admin role
Audit role assignmentsReview periodically who holds what
Keep administrators out of automated pathsNever put an administrator-conferring role in an IdP-managed group or a social-login default; the platform refuses it anyway

Standards used on this page​

Plain nameStandard
JSON Web Token (JWT)RFC 7519

See the standards reference for what each one covers and how LumoAuth uses it.