Febasi Docs
Concepts

Authorization

Hierarchical role-based access control — roles with levels, scope:action permissions, and the union model that decides what a user can do.

The Auth API uses hierarchical role-based access control (RBAC) with scope:action permissions. Roles carry a numeric level from 1 to 100 that controls who can manage whom. A user's effective permission set is the union of their role-derived permissions and any direct grants.

Vocabulary

TermDefinition
PermissionA scope:action pair like users:create, roles:assign, tenants:read.
RoleA named group of permissions, scoped to a tenant, with a level 1–100.
LevelInteger 1–100. Higher levels manage lower levels. System roles use 10/50/90/100.
Direct grantA permission attached to a single user, optionally with an expiresAt.

Permission format

Every permission is scope:action:

users:create
users:read
users:update
users:delete
roles:assign
roles:revoke
permissions:grant
permissions:revoke
tenants:update
client-keys:create
auth:logs

Custom permissions follow the same pattern. The Auth API ships with 24 system permissions seeded into every new tenant. Custom permissions live alongside them in the same table; they are distinguished by an is_system flag.

System roles

When a tenant is created, four roles are seeded:

RoleLevelNotes
super_admin100Full access. Cannot be deleted.
admin90Tenant administrator. Cannot manage super_admin.
manager50Can manage user-level accounts only.
user10Default end-user role.

System roles cannot be deleted or have their level changed. You can, however, grant additional permissions to a system role on a per-tenant basis.

The hierarchy rule

A user cannot manage another user, role, or grant whose level is greater than or equal to their own highest role level.

A manager (level 50) can:

  • Assign roles up to level 49.
  • Promote/demote a user (level 10).
  • Revoke permissions from a user.

A manager cannot:

  • Touch another manager, admin, or super_admin.
  • Create a custom role at level ≥ 50.
  • Grant permissions that aren't already in their own permission set.

Violations return HIERARCHY_VIOLATION (HTTP 403) with both the actor and target levels in the error payload — handy for client-side messages.

Effective permissions

A user's effective permission set is:

effectivePermissions = union(rolePermissions, directGrants)

Roles can be assigned to users with an optional expiresAt. Direct grants also support expiresAt. Expired entries are filtered out at read time and never embedded in newly issued tokens.

You can inspect the breakdown at any time:

GET /api/v1/permissions/user/{userId}
{
  "success": true,
  "data": {
    "userId": "01HXY...",
    "rolePermissions": ["users:read", "users:update"],
    "individualPermissions": ["client-keys:create"],
    "effectivePermissions": ["users:read", "users:update", "client-keys:create"]
  }
}

Authorization in code

Endpoints are protected by middlewares applied per route:

MiddlewareBehavior
requirePermission('users:create')Single permission check.
requireAnyPermission(['a:b','c:d'])OR — passes if user has any.
requireAllPermissions(['a:b','c:d'])AND — passes only if user has all.
requirePermissionDualAuth(...)Same as above, but accepts JWT or Client Key.

These checks read from the JWT payload, not the database — so the most recent permission edit only takes effect after the user's next access-token refresh (max ~15 minutes later by default). For checks that must be real-time, call POST /api/v1/permissions/check and compare against the live database.

How to grant or revoke

POST /api/v1/permissions/grant
{
  "userId": "01HXY...",
  "permissionId": "01HZ1...",
  "expiresAt": "2026-12-31T23:59:59Z"
}
POST /api/v1/permissions/revoke
{
  "userId": "01HXY...",
  "permissionId": "01HZ1..."
}
POST /api/v1/roles/assign
{
  "userId": "01HXY...",
  "roleId": "01HZ2...",
  "expiresAt": null
}
POST /api/v1/roles/remove
{
  "userId": "01HXY...",
  "roleId": "01HZ2..."
}

Both grants and revocations write to the audit log.

On this page