This document describes the Role-Based Access Control (RBAC) system implemented in the mobile-money backend service. The RBAC system provides fine-grained access control to API endpoints based on user roles and permissions.
The system defines three primary roles:
- Description: Full access to all system resources
- Permissions: All available permissions
- Use Case: System administrators, superusers
- Access Level: Complete system control
- Description: Read/write access to own data
- Permissions:
read:own,write:own,delete:own - Use Case: Regular mobile money users
- Access Level: Personal data management
- Description: Read-only access to public data
- Permissions:
read:all - Use Case: Auditors, read-only stakeholders
- Access Level: View-only access
Permissions define specific actions that users can perform:
| Permission | Description | Typical Roles |
|---|---|---|
read:own |
Read user's own data | user, admin |
write:own |
Create/update user's own data | user, admin |
delete:own |
Delete user's own data | user, admin |
read:all |
Read all system data | viewer, admin |
write:all |
Write/update all system data | admin |
delete:all |
Delete any system data | admin |
| Permission | Description | Typical Roles |
|---|---|---|
admin:system |
Full system administration | admin |
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE role_permissions (
role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
permission_id UUID REFERENCES permissions(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (role_id, permission_id)
);-- Added role_id foreign key
ALTER TABLE users
ADD COLUMN role_id UUID REFERENCES roles(id);JWT tokens now include role information:
{
"userId": "uuid",
"email": "user@example.com",
"role": "user|admin|viewer",
"iat": 1234567890,
"exp": 1234571490
}import { requirePermission } from "../middleware/rbac";
// Require specific permission
router.get("/transactions", requirePermission("read:own"), getTransactions);
router.post("/transactions", requirePermission("write:own"), createTransaction);import { requireRole } from "../middleware/rbac";
// Require specific role
router.get("/admin/users", requireRole("admin"), getAllUsers);import { requireAnyPermission } from "../middleware/rbac";
// Require any of the specified permissions
router.get("/data", requireAnyPermission(["read:own", "read:all"]), getData);import { attachUserContext } from "../middleware/rbac";
// Attach role and permissions without blocking
router.get("/profile", authenticateToken, attachUserContext, getProfile);The system provides several pre-configured middleware functions:
requireAdmin: Requires admin rolerequireReadAccess: Requiresread:ownorread:allpermissionrequireWriteAccess: Requireswrite:ownorwrite:allpermissionrequireOwnDataAccess(action): Requires${action}:ownpermission
POST /api/auth/login
Content-Type: application/json
{
"phone_number": "+237123456789"
}Response:
{
"message": "Login successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"userId": "uuid",
"phone_number": "+237123456789",
"kyc_level": "unverified",
"role": "user"
}
}GET /api/auth/me
Authorization: Bearer <token>Response:
{
"user": {
"userId": "uuid",
"phone_number": "+237123456789",
"kyc_level": "unverified",
"role": "user",
"permissions": ["read:own", "write:own", "delete:own"]
},
"tokenInfo": {
"issuedAt": 1234567890,
"expiresAt": 1234571490
}
}GET /api/admin/users
Authorization: Bearer <admin-token>GET /api/transactions
Authorization: Bearer <user-token>{
"error": "Unauthorized",
"message": "Authentication required"
}{
"error": "Forbidden",
"message": "Insufficient permissions. Required: read:all",
"userRole": "user",
"userPermissions": ["read:own", "write:own", "delete:own"]
}- Token Validation: Always validate JWT tokens before checking permissions
- Permission Caching: Consider caching user permissions for performance
- Role Changes: Role changes take effect on next login (token refresh)
- Database Security: Ensure proper database permissions for RBAC tables
- Audit Logging: Log permission checks for security auditing
To set up RBAC in your database:
-
Run the main schema:
psql -d your_database -f database/schema.sql
-
Run the seed migration:
psql -d your_database -f database/migrations/001_seed_rbac.sql
This will create the necessary tables and populate them with default roles and permissions.
The RBAC system can be tested using different user roles:
- Admin User: Full access to all endpoints
- Regular User: Access to own data only
- Viewer User: Read-only access to public data
Use the login endpoint to generate tokens for different roles and test access controls accordingly.
- Dynamic Permissions: Runtime permission management
- Resource-based Access Control: More granular resource permissions
- Permission Inheritance: Role hierarchy support
- Time-based Access: Temporary permissions
- IP-based Restrictions: Location-based access control