June 13, 2026
How to Build a Role-Based Access Control (RBAC) System
A step-by-step guide to designing secure, scalable authorization systems using roles, permissions, and best practices.
Ushani Saubhagya
5 min read
Introduction
Security is one of the most important aspects of modern software development. Whether you're building a social media platform, an e-commerce website, a banking app or an enterprise management system, controlling who can access specific resources is crucial.
Think of a company management system where employees can view their profiles, managers can manage their teams, and administrators can control the entire system. Without proper access controls, any user can gain unauthorized access to sensitive information or administrative tasks.
This is where role-based access control (RBAC) comes into play.
RBAC is one of the most commonly used permission models in software applications. It simplifies permission management by assigning permissions to roles rather than directly to users. Instead of granting individual permissions to each user, users are assigned roles and the roles determine the actions they can perform.
In this article, we will explore RBAC, understand its architecture and learn how to build a scalable RBAC system that can be integrated into modern web applications.
What is Role-Based Access Control (RBAC)?
Role-Based Access Control (RBAC) is a permission system that restricts system access based on the role assigned to a user.
A role represents a set of permissions that define what actions a user can perform.
For example:
Instead of granting permissions directly to users, permissions are grouped into roles and users are assigned one or more roles.
This approach makes systems easier to manage, especially when dealing with thousands of users.
Authentication vs Authorization
Before building an RBAC system, it is important to understand the difference between authentication and authorization.
Authentication
Verification answers the question:
"Who are you?"
Examples:
· Logging in with email and password
· Using Google Sign-in
· Using JWT tokens
When authentication is successful, the system verifies the user's identity.
Authorization
Permission answers the question:
"What are you allowed to do?"
Examples:
· Can the user delete a product?
· Can the user access the admin dashboard?
· Can the user modify another user's account?
RBAC is an authorization mechanism.
Why Use RBAC?
Many beginners start by hardcoding permissions.
Examples:
if user.email == "admin@company.com":
allow_access()if user.email == "admin@company.com":
allow_access()This works initially but becomes unmaintainable as the application grows.
RBAC provides several advantages:
-
Simplified permission management: Permissions are managed through roles rather than individual users.
-
Better security: Users only receive the permissions they need for their responsibilities.
-
Scalability: It is easy to add new users because roles already define access levels.
-
Compliance: Many organizations require strict access controls to meet security regulations.
-
Easier maintenance: Changing permissions for a role automatically affects all users assigned to that role.
RBAC Architecture
A standard RBAC system consists of four components:
Users
Individuals who access the system.
Examples:
- John
- Sarah
- Michael
Roles
Categories that define access levels.
Examples:
- Admin
- Manager
- Employee
Permissions
Specific actions users are allowed to perform.
Examples:
- create_user
- update_user
- delete_user
- view_reports
Resources
Objects or modules being protected.
Examples:
- Users
- Products
- Orders
- Reports
The relationship looks like this:
User
↓
Role
↓
Permission
↓
ResourceUser
↓
Role
↓
Permission
↓
ResourceDatabase Design for RBAC
A scalable RBAC implementation typically includes multiple tables.
Users Table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100),
email VARCHAR(255)
);CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100),
email VARCHAR(255)
);Roles Table
CREATE TABLE roles (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE
);CREATE TABLE roles (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE
);Example data:
1 Admin
2 Manager
3 Employee1 Admin
2 Manager
3 EmployeePermissions Table
CREATE TABLE permissions (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE
);CREATE TABLE permissions (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE
);Example:
create_user
delete_user
update_user
view_reportscreate_user
delete_user
update_user
view_reportsUser Roles Table
Many users can have many roles.
CREATE TABLE user_roles (
user_id INTEGER,
role_id INTEGER
);CREATE TABLE user_roles (
user_id INTEGER,
role_id INTEGER
);Role Permissions Table
Many roles can have many permissions.
CREATE TABLE role_permissions (
role_id INTEGER,
permission_id INTEGER
);CREATE TABLE role_permissions (
role_id INTEGER,
permission_id INTEGER
);RBAC Workflow
The authorization process follows these steps:
Step 1
User logs into the application.
Step 2
Authentication system verifies credentials.
Step 3
System retrieves the user's assigned roles.
Step 4
Roles are mapped to permissions.
Step 5
Application checks whether the required permission exists.
Step 6
Access is granted or denied.
Workflow:
Login
↓
Authenticate User
↓
Fetch Roles
↓
Fetch Permissions
↓
Check Permission
↓
Allow / DenyLogin
↓
Authenticate User
↓
Fetch Roles
↓
Fetch Permissions
↓
Check Permission
↓
Allow / DenyImplementing RBAC in Backend Applications
Let's build a simple RBAC system using Python.
Define Roles
ROLES = {
"admin": [
"create_user",
"delete_user",
"update_user",
"view_reports"
],
"manager": [
"update_user",
"view_reports"
],
"employee": [
"view_profile"
]
}ROLES = {
"admin": [
"create_user",
"delete_user",
"update_user",
"view_reports"
],
"manager": [
"update_user",
"view_reports"
],
"employee": [
"view_profile"
]
}User Object
user = {
"username": "john",
"role": "manager"
}user = {
"username": "john",
"role": "manager"
}Permission Check Function
def has_permission(user, permission):
role = user["role"]
return permission in ROLES.get(role, [])def has_permission(user, permission):
role = user["role"]
return permission in ROLES.get(role, [])Usage
if has_permission(user, "view_reports"):
print("Access Granted")
else:
print("Access Denied")if has_permission(user, "view_reports"):
print("Access Granted")
else:
print("Access Denied")Output:
Access GrantedAccess GrantedRBAC in REST APIs
Modern web applications often expose REST APIs.
Consider the endpoint:
DELETE /api/users/123DELETE /api/users/123Only administrators should perform this action.
Example:
@app.delete("/users/{id}")
def delete_user(id, current_user):
if not has_permission(
current_user,
"delete_user"
):
raise Exception("Forbidden")
return {"message": "Deleted"}@app.delete("/users/{id}")
def delete_user(id, current_user):
if not has_permission(
current_user,
"delete_user"
):
raise Exception("Forbidden")
return {"message": "Deleted"}Permission check occurs before the action is executed.
RBAC with JWT Authentication
Many applications use JSON Web Tokens (JWT).
Example token payload:
{
"user_id": 1,
"role": "admin"
}{
"user_id": 1,
"role": "admin"
}After signing in:
· The user receives a JWT token.
· The token contains information about the role.
· The secure endpoint verifies permissions.
Example:
decoded_token = jwt.decode(token)
role = decoded_token["role"]decoded_token = jwt.decode(token)
role = decoded_token["role"]Then perform authorization checks using the role.
Middleware-Based RBAC
As applications grow, permission checks should not be repeated everywhere. A middleware approach centralizes permission. Example concept:
@require_permission("delete_user")
def delete_user():
pass@require_permission("delete_user")
def delete_user():
passDecorator:
def require_permission(permission):
def wrapper(func):
def inner(*args, **kwargs):
if not has_permission(
current_user,
permission
):
raise Exception("Forbidden")
return func(*args, **kwargs)
return inner
return wrapperdef require_permission(permission):
def wrapper(func):
def inner(*args, **kwargs):
if not has_permission(
current_user,
permission
):
raise Exception("Forbidden")
return func(*args, **kwargs)
return inner
return wrapperBenefits:
· Clean code
· Reusable logic
· Easy maintenance
Hierarchical Roles
Many organizations use role inheritance.
Example:
Admin
↑
Manager
↑
EmployeeAdmin
↑
Manager
↑
EmployeeAn Admin automatically inherits:
- Manager permissions
- Employee permissions
This reduces duplication.
Example:
Employee:
view_profile
Manager:
view_profile
view_reports
Admin:
view_profile
view_reports
delete_userEmployee:
view_profile
Manager:
view_profile
view_reports
Admin:
view_profile
view_reports
delete_userCommon RBAC Models
Basic RBAC
Users are assigned roles. Roles contain permissions. Many applications use this model.
Hierarchical RBAC
Roles inherit permissions from lower-level roles. Suitable for large organizations.
Constrained RBAC
Adds restrictions.
Example:
A user cannot approve their own expense request. Useful for banking and finance.
Symmetric RBAC
Supports management of permissions and roles through centralized administration. Often used in enterprise systems.
RBAC in Real-World Applications
E-Commerce Platforms
Admin:
- Manage products
- Manage orders
- Manage users
Customer:
- View products
- Place orders
Hospital Systems
Doctor:
- View patient records
- Create prescriptions
Nurse:
- View patient records
Admin:
- Manage staff
Learning Management Systems
Student:
- Access courses
Instructor:
- Create courses
- Grade assignments
Admin:
- Manage platform
Banking Systems
Customer:
- View accounts
- Transfer funds
Manager:
- Approve transactions
Admin:
- System management
Security Best Practices
Building RBAC incorrectly can be risky. Follow these best practices.
Principle of Least Privilege
Users should only receive the permissions they need to perform their tasks. Avoid granting administrator access unnecessarily.
Deny by Default
If no permission is found:
return Falsereturn FalseNever allow access by default.
Validate on Server Side
Never rely solely on frontend checks.
Bad:
if(user.role === "admin")if(user.role === "admin")Frontend checks can be bypassed.
Always validate permissions on the backend.
Audit Logging
Record important actions.
Example:
User John deleted account #234
Time: 2025-08-10 15:22User John deleted account #234
Time: 2025-08-10 15:22This helps with investigations and compliance.
Regular Permission Reviews
Roles evolve over time.
Periodically verify:
- Role assignments
- Permission mappings
- Unused roles
RBAC Limitations
Although RBAC is powerful, it has limitations.
Role Explosion
Large organizations may create hundreds of roles.
Example:
JuniorManager
SeniorManager
RegionalManager
BranchManagerJuniorManager
SeniorManager
RegionalManager
BranchManagerManaging many roles becomes difficult.
Lack of Context Awareness
RBAC only considers roles.
It does not consider:
- Time
- Location
- Device
- Ownership
Example: A manager can only edit reports during working hours. RBAC alone cannot enforce this.
RBAC vs ABAC
Attribute-Based Access Control (ABAC) extends RBAC.
RBAC:
Role = Manager
Access = AllowedRole = Manager
Access = AllowedABAC:
Role = Manager
Department = Sales
Location = Office
Time = Working HoursRole = Manager
Department = Sales
Location = Office
Time = Working HoursABAC provides more flexibility but is more complex.
For most applications, RBAC is sufficient.
Conclusion
Role-Based Access Control (RBAC) is one of the most important authorization mechanisms in modern software systems. This enables a structured and scalable way of managing permissions by assigning permissions to roles, instead of managing permissions for individual users.
A well designed RBAC system adds security, eases administration, and scales nicely with growing applications. The basic idea is simple: users are assigned roles, roles have permissions and permissions define what actions can be performed on protected resources.
The implementation of RBAC should focus on a good database design, centralized permission checks, validation on the server side and the principle of least privilege. As your app matures, role hierarchies and middleware-based authorization can help improve maintainability and security still further.