---
term: 'Role-Based Access Control (RBAC)'
seoTitle: 'RBAC Explained: Roles, NIST Model, Role Explosion, Design'
headline: 'What is Role-Based Access Control (RBAC)?'
slug: role-based-access-control-rbac
category: auth-security
shortDefinition: 'Role-based access control is an authorization model where permissions attach to roles, and users get permissions only through their roles.'
relatedTerms:
  - access-control-lists-acl
  - class-level-permissions-clp
  - identity-access-management-iam
  - row-level-security
contrastsWith:
  - access-control-lists-acl
aboutTerms:
  - 'Role Hierarchy'
  - 'Separation of Duties'
  - 'Role Explosion'
faq:
  - question: 'What is RBAC in simple terms?'
    answer: 'Access is granted by job function, not per person: permissions attach to roles — Editor, Admin, Support — and users inherit whatever their assigned roles carry. Hiring, promotion, and departure become role changes in one place instead of permission edits scattered everywhere.'
  - question: 'What is an example of RBAC?'
    answer: 'A content platform with three roles: Admin (everything), Editor (create, edit, publish), Viewer (read only). Ada the editor publishes because Editor carries publish — not because anyone granted Ada anything directly. Reassign her to Viewer and every editing permission vanishes in a single update.'
  - question: 'What is the difference between RBAC and ABAC?'
    answer: 'RBAC decides from predefined roles; attribute-based access control evaluates attributes of the user, resource, and context — department, sensitivity, time — at request time. RBAC is simpler to reason about and audit; ABAC is finer-grained and harder to debug. Mature systems use RBAC as the baseline and add attribute conditions where context genuinely matters.'
  - question: 'What is the difference between RBAC and an ACL?'
    answer: 'Direction of attachment: an ACL hangs subject-permission entries on each object; RBAC hangs permissions on roles across the system. They compose rather than compete — an ACL entry can name a role, which is how per-object control and one-place membership management coexist.'
  - question: 'What are the RBAC models?'
    answer: 'The standard defines core RBAC (users, roles, permissions, sessions), hierarchical RBAC (senior roles inherit junior roles'' permissions), and constrained RBAC (separation-of-duty rules — static constraints on assignment, dynamic constraints on what one session may activate together).'
  - question: 'What are the three rules of RBAC?'
    answer: 'From the original 1992 formulation: a subject can act only through a selected role (role assignment); the subject must be authorized for that role (role authorization); and an action is allowed only if the active role holds its permission (permission authorization). Together: no access except through roles.'
  - question: 'What is role explosion?'
    answer: 'Uncontrolled role growth when every exception, project, region, or tenant spawns a new role — Project-A-Manager-Region-West — until roles outnumber users and nobody can audit the system. The root cause is encoding contextual attributes as roles instead of handling them with conditions or per-object entries.'
  - question: 'Is RBAC the same as least privilege?'
    answer: 'No — least privilege is the principle, RBAC is one mechanism for pursuing it, and only role discipline connects them. An over-broad role violates least privilege from inside RBAC; scoping roles minimally and reviewing them periodically is what actually delivers the principle.'
  - question: 'What is separation of duties in RBAC?'
    answer: 'Constraints that keep conflicting powers apart: static separation blocks one user from ever holding both payment-creator and payment-approver; dynamic separation allows holding both but never activated in the same session. It is fraud prevention expressed as a role rule.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'NIST Role-Based Access Control project'
    url: 'https://csrc.nist.gov/projects/role-based-access-control'
  - name: 'Ferraiolo & Kuhn — Role-Based Access Control (1992)'
    url: 'https://csrc.nist.gov/CSRC/media/Projects/Role-Based-Access-Control/documents/ferraiolo-kuhn-92.pdf'
  - name: 'NISTIR 7316 — Assessment of Access Control Systems'
    url: 'https://nvlpubs.nist.gov/nistpubs/legacy/ir/nistir7316.pdf'
  - name: 'OWASP Authorization Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html'
cta:
  title: 'Roles you can query'
  text: 'Back4app roles are database objects: add members with a relation, nest roles for hierarchy, and grant through class permissions and object ACLs — RBAC enforced by the platform on every request.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-27'
translationKey: role-based-access-control-rbac
---

**Role-based access control is an authorization model where permissions attach to roles, and users get permissions only through their roles.** The indirection is the entire idea: nothing is ever granted to a person directly, so organizational change becomes data change — a promotion is a role reassignment, not an archaeology dig through scattered grants. Proposed by [Ferraiolo and Kuhn in 1992](https://csrc.nist.gov/CSRC/media/Projects/Role-Based-Access-Control/documents/ferraiolo-kuhn-92.pdf) as an alternative to the older discretionary and mandatory models, it became the ANSI/INCITS 359 standard and the default authorization vocabulary of enterprise software.

## Key takeaways

| Question | Answer |
| --- | --- |
| The indirection | User → role → permission — never user → permission directly |
| Role ≠ group | A group collects *users*; a role collects *permissions* |
| The model levels | Core · hierarchical (inheritance) · constrained (separation of duties) |
| The failure mode | Role explosion — attributes encoded as roles until roles outnumber users |
| The composition | Roles inside ACL entries: RBAC for the many, ACLs for the exceptions |

## Users, roles, permissions — the indirection at work

```text
Permissions                 Roles                    Users
─────────────               ─────────────            ─────────────
posts:read        ─┐
posts:write        ├──▶     Editor          ◀──────  Ada, Grace
posts:publish     ─┘
users:manage      ─┐
billing:view       ├──▶     Admin           ◀──────  Linus
posts:*           ─┘
posts:read        ────▶     Viewer          ◀──────  everyone else

Ada publishes because Editor carries posts:publish — reassign her role,
and every permission it carried moves with it. One edit, not N.
```

Roles as live data, wired into object permissions:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Roles as data: create a role, add members, grant through it
const roleACL = new Parse.ACL();
roleACL.setPublicReadAccess(true);
const editors = new Parse.Role('Editors', roleACL);
editors.getUsers().add(adaUser);
await editors.save();

// Grant by role, not by user — membership changes in one place
const post = new Parse.Object('Post');
const acl = new Parse.ACL(currentUser);  // owner entry
acl.setRoleWriteAccess('Editors', true); // RBAC meets the object's ACL
post.setACL(acl);
await post.save();
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Roles as data: create a role, add members, grant through it
final roleACL = ParseACL()..setPublicReadAccess(allowed: true);
final editors = ParseObject('_Role')
  ..set('name', 'Editors')
  ..setACL(roleACL)
  ..addRelation('users', [adaUser]);
await editors.save();

// Grant by role, not by user — membership changes in one place
final post = ParseObject('Post');
final acl = ParseACL(owner: currentUser)  // owner entry
  ..setRoleWriteAccess('Editors', true);  // RBAC meets the object's ACL
post.setACL(acl);
await post.save();
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Roles as data: create a role, add members, grant through it
var editors = try ParseRole(name: "Editors")
let savedRole = try await editors.save()
try await savedRole.users.add([adaUser]).save() // membership = a relation

// Grant by role, not by user — membership changes in one place
var post = Post()
var acl = ParseACL()
acl.setReadAccess(user: currentUser, value: true)
acl.setWriteAccess(user: currentUser, value: true)   // owner entry
acl.setWriteAccess(roleName: "Editors", value: true) // RBAC meets the ACL
post.ACL = acl
try await post.save()
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Roles as data: create a role, add members, grant through it
val roleACL = ParseACL()
roleACL.publicReadAccess = true
val editors = ParseRole("Editors", roleACL)
editors.users.add(adaUser)
editors.save()

// Grant by role, not by user — membership changes in one place
val post = ParseObject("Post")
val acl = ParseACL(ParseUser.getCurrentUser()) // owner entry
acl.setRoleWriteAccess("Editors", true)        // RBAC meets the object's ACL
post.acl = acl
post.save()
```

## The NIST model, correctly

Most explainers compress the standard into a listicle; the actual structure is worth thirty seconds. **Core RBAC** defines users, roles, permissions — and *sessions*, the forgotten element: a user activates a subset of their roles per session, which is how an admin can browse with member powers by default and escalate deliberately. The original paper's three rules bind it together: act only through a role, hold only authorized roles, do only what the active role permits. **Hierarchical RBAC** adds inheritance — senior roles subsume junior ones (Manager ⊇ Employee), removing duplication. **Constrained RBAC** adds separation of duties: *static* rules forbid conflicting role assignments outright (never both payment-creator and payment-approver); *dynamic* rules allow the assignment but forbid activating both in one session. And NIST's sharpest clarification, routinely garbled: a **group is a collection of users; a role is a collection of permissions** — the role is defined by what it can do, not who is in it.

```mermaid
flowchart LR
  accTitle: RBAC structure with sessions and separation of duties
  accDescr: Users are assigned roles and activate a subset of them per session. Roles carry permissions and can inherit from junior roles. Separation-of-duty constraints restrict which roles can be assigned or activated together, and permissions apply to resources.
  U["User<br/>Ada"] -->|"assigned"| R["Roles<br/>Editor · Auditor"]
  U -->|"activates subset<br/>per session"| S["Session<br/>Editor only"]
  R -->|"inherits"| RJ["Junior role<br/>Viewer"]
  R ---|"SoD constraint:<br/>not with Approver"| X["Conflicting role"]
  S -->|"permissions of<br/>active roles"| P["posts:write<br/>posts:publish"] --> D[("Resources")]
```

## RBAC vs. ACL vs. ABAC

| | RBAC | [ACL](/glossary/access-control-lists-acl/) | ABAC |
| --- | --- | --- | --- |
| Permissions attach to | Roles | Each object | Attribute rules |
| Native question | What can this *function* do? | Who can touch *this object*? | Is this allowed *in context*? |
| Administration | One place per role | Per object | Per policy |
| Per-object sharing | Can't express it | Its home game | Expressible, verbose |
| Audit "what can Ada do?" | Read her roles | Scan every object | Evaluate every rule |
| Failure mode | Role explosion | List sprawl | Opaque policies |

The insight the vendor SERP buries: these **compose, they don't compete**. Roles handle access that follows function; ACL entries handle per-object exceptions — and the hinge is the *role-entry ACE*, an ACL line naming a role instead of a user, giving object-level control with one-place membership. Attribute conditions layer on top where context (time, tenant, record state) genuinely decides. "Which one?" is usually the wrong question; "which layer handles which decision?" is the design.

## Role explosion — the failure mode

RBAC's characteristic disease: every exception mints a role, then every project, region, and tenant multiplies them — `Project-A-Manager-Region-West-ReadOnly` — until roles outnumber users and the audit answer to "who can do what?" is "nobody knows." The root cause is always the same: **contextual attributes encoded as roles**. The mitigations, in order: keep attributes out of role names (region and tenant are conditions or scopes, not roles); handle per-object sharing with [ACL entries](/glossary/access-control-lists-acl/), never per-object roles; scope roles per [tenant](/glossary/tenant-isolation/) structurally instead of by name-mangling; and audit — roles nobody holds, permissions no role uses, and grants nobody remembers are drift, and drift is how least privilege quietly dies. A working heuristic: if a role list stops fitting on one screen, the model is absorbing work that belongs to another layer.

## Designing roles: top-down, bottom-up, or both

The part no ranking explainer covers: where roles come from. **Top-down** derives them from the org and its processes — interview the business, name the functions, assign minimal permissions; accurate but slow. **Bottom-up** mines them from existing grants — cluster who already holds what, and candidate roles fall out; fast but launders past mistakes into policy. Practice is hybrid: mine for candidates, validate against functions, then apply the 80/20 test — a handful of broad roles for the organization's bulk, exceptions handled by ACLs or conditions rather than boutique roles. And one implementation rule that outlives every reorg: **code should check permissions, not role names** — `can('posts:publish')`, not `hasRole('Editor')` — so redefining a role is a data change, not a refactor, with enforcement [server-side](/glossary/data-layer-vs-application-layer-security/), deny-by-default.

## Common use cases

- **Admin panels and back-offices** — support, moderator, finance, superadmin: functions map cleanly to roles.
- **Content and publishing workflows** — author, editor, publisher, with separation between writing and releasing.
- **B2B SaaS team permissions** — owner/admin/member/billing per workspace, scoped per tenant.
- **Compliance-bound operations** — separation of duties as enforceable role constraints, with role membership as the audit artifact.
- **The role-gate on data layers** — roles as the "who" in [class-level permissions](/glossary/class-level-permissions-clp/) and [row-level policies](/glossary/row-level-security/).

## Should you use RBAC? A decision matrix

| Situation | Lean |
| --- | --- |
| Access follows job function | RBAC — its home game |
| Users share individual records ad hoc | ACLs — roles can't express it |
| Context decides (time, state, tenant) | Attribute conditions over the roles |
| A handful of user types, stable | RBAC with 3–7 broad roles |
| Deep org charts, nested teams | Role hierarchy — or ReBAC at real scale |
| "Just admins and everyone else" | One role gate — don't over-model it |

## Limitations and trade-offs

- **Per-object sharing is out of scope.** "Share this document with Ana" has no RBAC answer short of a role per document — that decision belongs to ACLs.
- **Same role, same powers.** Two Editors are indistinguishable; individual nuance requires another layer, not a near-duplicate role.
- **Role engineering is real upfront work.** Skipping it yields roles that mirror neither the org nor the risk model — and get copied forever.
- **Static roles miss dynamic risk.** Role membership doesn't see unusual hours, new devices, or sensitive record states; that's attribute territory.
- **Drift is the steady state.** Without periodic recertification, role scope only grows; RBAC's auditability is a capability, not a guarantee.

## Roles on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Roles here are **data, not code**: each is an object in the `_Role` class with a `users` relation for members and a `roles` relation for nesting — and nesting is hierarchy for free, since members of a child role inherit whatever its parent roles are granted. The grants themselves happen exactly where the composition section points: role names appear in [class-level permissions](/glossary/class-level-permissions-clp/) for the broad strokes and in per-object [ACL entries](/glossary/access-control-lists-acl/) for the exceptions — the code tabs show both halves — with Back4app enforcing the result on every REST, GraphQL, and Live Query request. Because roles are queryable objects, membership management, audits, and admin UIs are ordinary database work: role explosion prevention as a data-modeling habit, not a governance project.
