CHAPTER 09
Intermediate
Role-Based Access Control (RBAC)
Updated: May 14, 2026
35 min read
# CHAPTER 9
Role-Based Access Control (RBAC)
1. Introduction
Once a user is authenticated, the system must decide what actions they are permitted to perform. Allowing any logged-in user to access the "Delete Database" button is a recipe for disaster. To manage permissions systematically across hundreds or thousands of users, the industry standard is Role-Based Access Control (RBAC). In this chapter, we will design database schemas to support RBAC, understand the hierarchy of roles and permissions, and build authorization middleware to protect sensitive endpoints.2. Learning Objectives
By the end of this chapter, you will be able to:- Define the principles of Role-Based Access Control (RBAC).
- Distinguish between Users, Roles, and Permissions.
- Design database architectures to support RBAC (Many-to-Many relationships).
- Implement an Authorization middleware function in Node.js/Express.
- Enforce granular access control on API routes.
3. Beginner-Friendly Explanation
Imagine a Hospital.- The User: Dr. Smith, Nurse Jones, and Janitor Bob.
- The Permissions: "Prescribe Medicine," "View Patient Records," "Clean Operating Room."
- The Role: We create a "Doctor" Role and attach the "Prescribe Medicine" and "View Patient Records" permissions to it.
4. The RBAC Database Architecture
Implementing RBAC usually requires a relational database (like MySQL) with a Many-to-Many architecture.You need three primary tables:
- 1. Users Table: Contains the user's email and password.
-
2.
Roles Table: Contains the roles (e.g.,
Admin,Editor,Viewer).
- 3. UserRoles Table (Pivot/Junction Table): Links a specific User ID to a specific Role ID.
*Advanced Implementations:* Large enterprises add a fourth and fifth table: Permissions Table (e.g., deletepost) and a Role_Permissions Pivot Table to link specific actions to specific roles.
5. Simple Implementation: The Role Column
For smaller projects, you can skip complex Pivot tables and simply add arole column directly to the users table. We will use this approach for our coding example.
sql
6. Passing the Role in the JWT
When a user logs in, we query the database, find their role, and embed it directly into the JWT payload.
javascript
7. Building the Authorization Middleware (AuthZ)
We already builtauthenticateToken to verify the JWT. Now we build a factory function that creates specific Role-checkers.
javascript
8. Protecting Routes with RBAC
Now we can chain our middlewares together sequentially. The request must pass AuthN (is the token valid?) AND AuthZ (is the role allowed?) before reaching the logic.
javascript
9. Backend Workflow: Dynamic Permissions (ABAC)
RBAC is excellent, but sometimes roles aren't enough. What if an "Editor" is only allowed to edit posts *they wrote*, but not posts written by other Editors? This requires Attribute-Based Access Control (ABAC). Inside the route handler, you must query the database for the specific post, check theauthorid of the post, and compare it to the req.user.userId. If they don't match, return a 403 Forbidden error.
10. Best Practices
- Fail Closed, Not Open: When designing authorization logic, your default assumption must be "Deny Access." You should only grant access if a specific, positive condition is met. If an error occurs during the authorization check, the system must fail "Closed" (deny access), never "Open" (grant access).
11. Common Mistakes
-
Trusting Client-Side Roles: Never rely on a React frontend sending
{ role: 'admin' }in a JSON payload. The client can easily manipulate this data using Developer Tools. Always extract the user's role from the mathematically verified JWT payload (req.user.role) or by querying the database directly on the server.
12. Exercises
- 1. Explain the architectural benefit of mapping Permissions to Roles, rather than mapping Permissions directly to individual Users.
13. Coding Challenges
-
Challenge: Look at the
authorizeRolesfactory function. Rewrite this conceptual logic in pure PHP. Write a functioncheckrole($requiredrole)that checks a hypothetical$SESSION['role']variable. If it does not match, useheader("HTTP/1.1 403 Forbidden");andexit().
14. MCQs with Answers
Question 1
In a standard RBAC (Role-Based Access Control) architecture, what is the hierarchical relationship between Users, Roles, and Permissions?
Question 2
When implementing Authorization middleware in Express.js, what HTTP status code is the industry standard for rejecting a request where the user is successfully authenticated, but their Role lacks the required permissions?
15. Interview Questions
- Q: Differentiate between Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC). Provide a specific scenario where RBAC is insufficient and ABAC must be utilized.
-
Q: Explain the design pattern of creating Authorization Middleware factory functions (like
authorizeRoles('admin', 'editor')) in Express.js. Why is this preferable to hardcodingif (req.user.role !== 'admin')inside every single route handler?