Implementing Role-Based Access Control (RBAC) Based on Use Cases
When building applications that serve multiple user roles—such as admin, driver, and customer—it's essential to control exactly who can perform which actions. Role-Based Access Control (RBAC) provides a structured and scalable way to manage permissions by linking user roles to the specific actions they are authorized to perform.
In my implementation, each role's permissions were carefully defined based on real-world use cases. For instance:
- Admins can manage users, assign roles, and access system-wide data.
- Drivers can view assigned tasks and update task statuses.
- Customers can create service requests and view their own information.
This article shares a general, framework-agnostic approach to RBAC that leverages clean architecture principles to build a maintainable and flexible authorization system.
Why Use a Use-Case Driven RBAC?
Many traditional RBAC implementations hardcode permissions directly, which can quickly become inflexible and hard to maintain as systems grow. Instead, I start by identifying the core use cases for each role, which answers the critical question:
"What actions should this role be allowed to perform in the system?"
For example:
- Admin: Manage users, assign roles, view system analytics.
- Driver: Access assigned tasks, update task progress.
- Customer: Submit service requests, review personal data.
Defining permissions through use cases ensures your authorization logic directly reflects the business needs. This leads to clearer, more maintainable access control that aligns with real-world workflows.
Clean Architecture: Separation of Concerns and Flow
To build an authorization system that is flexible, testable, and reusable across different frameworks or languages, I apply clean architecture principles by separating concerns into distinct layers, each responsible for specific tasks in the RBAC flow:
1. Domain Layer — Core Business Rules and Authorization Logic
- Responsibilities:
- Define the RBAC rules: which roles have permission to perform which actions.
- Enforce business logic without depending on external systems or protocols.
- Flow:
- Receives an authorization request from the application layer.
- Checks if the user's role(s) grant permission for the requested action.
- Returns a clear allow/deny decision back to the application layer.
This layer remains pure and reusable because it is decoupled from infrastructure concerns.
2. Application Layer — Coordination, Authentication, and Security
- Responsibilities:
- Authenticate users by verifying tokens or session data.
- Extract user identity and roles from authentication tokens.
- Determine what the requested action is based on the incoming request.
- Call the domain layer to check authorization.
- Handle the decision (allow or deny) by either proceeding with the business operation or returning an access denied response.
- Flow:
- Receives user requests from the infrastructure layer.
- Validates authentication credentials.
- Translates the request into an action to be authorized.
- Queries the domain layer for authorization decision.
- Acts accordingly based on authorization results.
By managing security and coordination here, this layer keeps the domain layer focused on business rules.
3. Infrastructure Layer — Framework Integration and I/O Handling
- Responsibilities:
- Handle HTTP requests/responses, routing, middleware, and other framework-specific features.
- Manage session persistence, database access, and external service communication.
- Pass requests and user credentials to the application layer.
- Flow:
- Accepts incoming requests from clients.
- Extracts tokens or session info from headers or cookies.
- Forwards request data and user credentials to the application layer.
- Returns the response generated by the application layer back to the client.
By isolating framework-specific code here, the rest of the system remains portable and easier to maintain.
How RBAC Works: Layered Flow Summary
- Client Request: A user sends a request with an authentication token.
- Infrastructure Layer: Receives the request, extracts authentication details, and forwards them along with request data to the application layer.
- Application Layer: Authenticates the user, extracts roles, identifies the intended action, and requests authorization from the domain layer.
- Domain Layer: Applies RBAC rules to determine if the action is permitted for the user's roles.
- Application Layer: Receives authorization decision and either processes the request further or denies access.
- Infrastructure Layer: Sends the final response to the client.
- Auditing: Authorization decisions and denials are logged for monitoring and security purposes.
Benefits of This Approach
- Framework Agnostic: Authorization logic is independent of any backend framework or technology.
- Reusability: Domain rules can be reused across multiple projects or services.
- Maintainability: Centralized authorization logic simplifies updates and enhances clarity.
- Testability: Business rules can be tested independently of infrastructure.
- Aligned With Business Needs: Use-case driven roles ensure access control fits real-world workflows.
Conclusion
Separating authentication from authorization and applying clean architecture principles creates a modular, maintainable, and adaptable RBAC system. This approach makes it easier to scale your system, switch technologies, and keep your security aligned with business goals.


