Introduction
Authentication vs Authorization
What is Role-Based Authorization?
Why RBAC is Important
When Should You Use RBAC?
System Architecture Overview
Store Role in Session During Login
Create Role Guard Helper
Protect Admin Route
Protect Vendor Route
Role-Based Scope Grouping
Create Admin Middleware (Advanced)
Prevent Privilege Escalation
Database-Level Role Enforcement (Extra Secure)
Multi-Role Access Example
Real Production Role Example
Common RBAC Mistakes
Best Practices
Security Flow
RBAC vs Simple Login Check
Introduction
Authentication answers:
`“Who are you?”`
Authorization answers:
`“What are you allowed to do?”`
In real-world applications, just checking if a user is logged in is not enough. We must also control what actions each user can perform.
This is where Role-Based Access Control (RBAC) comes in.
In this guide, we will build a production-ready RBAC system using:
Actix-Web
Session-based authentication
Middleware + guard helpers
Clean architecture
2️⃣ What is Role-Based Authorization?
RBAC is a system where users are assigned roles, and roles define permissions.
Why RBAC is Important
Without RBAC:
Any logged-in user may access admin routes
Data leaks may happen
Privilege escalation becomes possible
With RBAC:
Clear separation of responsibilities
Secure system design
Easy to scale for large applications
4️⃣When Should You Use RBAC?
Use RBAC when:
You have multiple user types
You build admin panels
You build SaaS applications
You handle sensitive business logic
5️⃣ System Architecture Overview
User Login
↓
Session stores user_id + role
↓
Middleware/Guard checks role
↓
Access granted or denied
6️⃣ Step 1: Store Role in Session During Login
Inside login handler:
session.insert("role", user.role.clone())?;
Now every request carries role info in session.
7️⃣ Step 2: Create Role Guard Helper
Create in src/handlers/common.rs
use actix_session::Session;
use actix_web::{error, Result};
pub fn require_role(session: &Session, allowed_roles: &[&str]) -> Result<()> {
let role = session
.get::<String>("role")
.map_err(error::ErrorInternalServerError)?
.unwrap_or_default();
if !allowed_roles.contains(&role.as_str()) {
return Err(error::ErrorForbidden("Access denied"));
}
Ok(())
}
8️⃣ Step 3: Protect Admin Route
Example:
pub async fn admin_dashboard(
session: Session,
) -> Result<HttpResponse> {
require_role(&session, &["admin"])?;
Ok(HttpResponse::Ok().json(ApiResponse {
message: "Admin Dashboard".to_string(),
data: true,
}))
}
9️⃣ Step 4: Protect Vendor Route
require_role(&session, &["admin", "vendor"])?;
This allows both admin and vendor.
🔟 Step 5: Role-Based Scope Grouping
Instead of checking role in every handler, create role middleware.
Example:
web::scope("/api/admin")
.wrap(RequireLogin)
.wrap(RequireAdmin)
Now all /api/admin/* routes are admin-only.
Create Admin Middleware (Advanced)
pub struct RequireAdmin;
impl<S, B> Transform<S, ServiceRequest> for RequireAdmin
Inside call():
let role = session.get::<String>("role")?;
if role != Some("admin".to_string()) {
return Err(error::ErrorForbidden("Admin only"));
}
1️⃣2️⃣ Prevent Privilege Escalation
Never trust frontend role input.
❌ Wrong:
let role = body.role;
✅ Correct:
Always read role from session:
let role = session.get::<String>("role")?;
1️⃣3️⃣ Database-Level Role Enforcement (Extra Secure)
For sensitive operations, double-check role in DB.
let db_role = sqlx::query_scalar!(
"SELECT role FROM users WHERE id = ?",
user_id
).fetch_one(&state.pool).await?;
This prevents tampering.
1️⃣4️⃣ Multi-Role Access Example
require_role(&session, &["admin", "manager"])?;
Very flexible design.
1️⃣5️⃣ Real Production Role Example
SaaS System Roles:
Super Admin
Admin
Vendor
Staff
Customer
Each role has:
Different dashboards
Different APIs
Different data visibility
1️⃣6️⃣ Common RBAC Mistakes
❌ Checking role only on frontend
❌ Forgetting to protect one route
❌ Hardcoding role logic inside business code
❌ Mixing authentication and authorization logic
1️⃣7️⃣ Best Practices
Always check role server-side
Keep authorization logic separate
Use middleware for scalability
Log forbidden access attempts
1️⃣8️⃣ Security Flow
Login → Session(role stored)
↓
User hits /api/admin/users
↓
Middleware checks role
↓
Allowed or 403 Forbidden
Top comments (0)