Skip to main content

Role-Based Access Control

Ithbat IAM uses a resource:action permission model. Every protected operation requires a specific permission string. Roles are named collections of permissions. Users are granted access by assigning them one or more roles.


Permission model

Permissions follow the resource:action convention. There are three actions:

ActionDescription
readView or list the resource
writeCreate or update the resource
deleteDelete the resource

A user who holds user:write can create and update users. They cannot delete users without user:delete.

Complete permission reference

PermissionControls access to
user:readList and view user accounts
user:writeCreate and update user accounts
user:deleteDelete user accounts
role:readList and view roles
role:writeCreate and update roles, assign/revoke roles
role:deleteDelete roles
group:readList and view groups and group members
group:writeCreate/update groups, add/remove members, assign roles to groups
organization:readList and view organizations and members
organization:writeCreate/update organizations, manage members
tenant:readView tenant details, stats, and billing overview
tenant:writeUpdate tenant settings, create/suspend/delete tenants
tenant:deleteDelete tenants
settings:readView tenant settings, SAML configs, IdP configs, password policies
settings:writeUpdate settings, create SAML/IdP configs, manage IP whitelists
billing:readView subscription, invoices, usage, and payment methods
billing:writeCreate/change subscriptions, add/remove payment methods
oauth:readList and view OAuth 2.0 clients
oauth:writeCreate, update, and delete OAuth clients
webhook:readList webhooks and view delivery logs
webhook:writeCreate/update/delete webhooks, send test events, retry deliveries
feature:readView feature flags and tenant overrides
feature:writeCreate/update/delete feature flags and tenant overrides
invitation:readList and view invitations
invitation:writeSend, resend, and revoke invitations
permission:readList available permissions
permission:writeCreate custom permissions
policy:readView access policies
policy:writeCreate, update, and delete policies
workflow:readView workflows, instances, and access requests
workflow:writeCreate/manage workflows, approve/reject access requests
directory:readView directory connections and sync jobs
directory:writeCreate/update directory connections, trigger syncs
scim:readView SCIM configuration, tokens, and logs
scim:writeUpdate SCIM configuration, create/revoke tokens
email_template:readView email templates
email_template:writeUpdate and preview email templates
content:readView content items
content:writeCreate, update, publish, and archive content
audit:readView audit logs and login history
log:readQuery and export raw logs
analytics:readView analytics dashboards

Admin wildcard

The special admin permission grants access to all resources within a tenant. A role containing admin does not need any other permissions listed above. The check is:

hasPermission = permissions.includes('admin') || permissions.includes('system:admin') || permissions.includes(requiredPermission)

Built-in roles

Ithbat IAM includes three system roles that cannot be deleted.

RolePermissionsDescription
Tenant Adminadmin (wildcard)Full access to all tenant resources
Useruser:read, content:readStandard member — can view their own profile and public content
System Adminsystem:adminPlatform-level admin (not assignable per-tenant; platform admin console only)

Creating custom roles

Custom roles let you apply the principle of least privilege. For example, a Support Agent role that can read users but not modify them.

Via admin console

  1. Go to Roles → Create Role.
  2. Enter a Name and optional Description.
  3. Select individual permissions from the permission tree.
  4. Click Save.

Via API

POST /api/v1/roles
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json

{
"name": "Support Agent",
"description": "Read-only access to users and audit logs"
}

After creating the role, assign permissions by updating it:

PUT /api/v1/roles/{id}
Authorization: Bearer {token}
Content-Type: application/json

{
"name": "Support Agent",
"description": "Read-only access to users and audit logs",
"permissions": ["user:read", "audit:read"]
}

Response:

{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"tenantId": "c2e3f4a5-...",
"name": "Support Agent",
"description": "Read-only access to users and audit logs",
"permissions": ["user:read", "audit:read"],
"isSystem": false,
"createdAt": "2026-02-24T10:00:00Z",
"updatedAt": "2026-02-24T10:00:00Z"
}

Assigning roles to users

Via admin console

  1. Go to Users → {user} → Roles.
  2. Click Assign Role and select from available roles.

Via API

POST /api/v1/users/{userId}/roles
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json

{
"roleId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

Remove a role from a user

DELETE /api/v1/users/{userId}/roles/{roleId}
Authorization: Bearer {token}

View a user's effective permissions

GET /api/v1/users/{userId}/permissions
Authorization: Bearer {token}

Response includes the union of all permissions from all roles assigned to the user:

{
"userId": "3fa85f64-...",
"permissions": ["user:read", "audit:read"]
}

Group-based access

Assign roles to groups and users inherit the role when they join the group. This is the recommended approach for managing access at scale.

Assign a role to a group

POST /api/v1/groups/{groupId}/roles
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json

{
"roleId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

View group's roles

GET /api/v1/groups/{groupId}/roles
Authorization: Bearer {token}

When a user is added to a group, they immediately inherit the group's roles. When removed, the inherited roles are revoked.


Organization-level permissions

Ithbat supports organizations within a tenant — sub-units such as departments or business units. A user can have different roles in different organizations.

Add a user to an organization with a specific role

POST /api/v1/organizations/{orgId}/members
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json

{
"userId": "3fa85f64-...",
"roles": ["7c9e6679-..."]
}

Update a member's organization-level roles

PUT /api/v1/organizations/{orgId}/members/{userId}/roles
Authorization: Bearer {token}
Content-Type: application/json

{
"roles": ["new-role-id"]
}

Checking permissions in your application

From the JWT access token

When a user authenticates, their permissions are embedded in the JWT access token claims. Decode the token to read permissions:

{
"sub": "3fa85f64-...",
"tenant_id": "c2e3f4a5-...",
"permissions": ["user:read", "audit:read"],
"exp": 1740391200
}

In JavaScript/TypeScript

function hasPermission(token: DecodedToken, permission: string): boolean {
return token.permissions.includes('admin') ||
token.permissions.includes('system:admin') ||
token.permissions.includes(permission);
}

if (hasPermission(decodedToken, 'user:write')) {
// show create user button
}

In backend middleware (Go example)

func RequirePermission(required string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := claimsFromContext(r.Context())
for _, p := range claims.Permissions {
if p == "admin" || p == "system:admin" || p == string(required) {
next.ServeHTTP(w, r)
return
}
}
http.Error(w, "forbidden", http.StatusForbidden)
})
}
}

Check permissions via API

For server-side authorization checks without decoding a JWT:

GET /api/v1/users/{userId}/permissions
Authorization: Bearer {token}

Access request workflows

Users without a required permission can submit an access request. A user with workflow:write permission then approves or rejects it.

Submit an access request

POST /api/v1/access-requests
Authorization: Bearer {user_token}
Content-Type: application/json

{
"requestedRoleId": "7c9e6679-...",
"reason": "Need access to run billing reports"
}

View pending approvals

GET /api/v1/access-requests/pending-approvals
Authorization: Bearer {approver_token}

Approve a request

POST /api/v1/access-requests/{id}/approve
Authorization: Bearer {approver_token}

Reject a request

POST /api/v1/access-requests/{id}/reject
Authorization: Bearer {approver_token}
Content-Type: application/json

{
"reason": "Access not required for your current role"
}

Role management API reference

List roles

GET /api/v1/roles
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}

Get a role

GET /api/v1/roles/{id}
Authorization: Bearer {token}

Get available permissions

Returns the full list of permissions that can be assigned to roles:

GET /api/v1/roles/permissions
Authorization: Bearer {token}

Delete a role

System roles (isSystem: true) cannot be deleted.

DELETE /api/v1/roles/{id}
Authorization: Bearer {token}

Best practices

Principle of least privilege: Grant only the permissions each role needs. Start with read-only access and add write permissions only when necessary.

Use groups for team-based access: Assign roles to groups (Engineering, Finance, Support) rather than individual users. When someone joins or leaves a team, update their group membership rather than their individual permissions.

Avoid assigning admin broadly: The admin wildcard grants full tenant access. Reserve it for your tenant administrators and use specific permissions for everyone else.

Audit role assignments regularly: Review who has user:delete, tenant:write, and billing:write permissions quarterly. Use the audit log to track role assignment history.

Use access request workflows for elevated access: Rather than permanently granting elevated permissions, route time-sensitive requests through the approval workflow.


Next steps

  • Multi-Tenancy — organization-level roles and cross-tenant permission model
  • Audit Logs — track role assignments, changes, and access decisions
  • User Management — manage users and their role assignments