Abdul Wahab

Blog/

Why Proper RBAC Matters More Than Just Login

Learn why true RBAC, not just login, is essential for secure multi-tenant SaaS and how to implement it correctly.

Why Proper RBAC Matters More Than Just Login

Understanding Auth vs. Authorization

Most apps brag about having “auth.” In reality they only have a login screen. Authentication answers the question who are you? Authorization , often shortened to RBAC (Role-Based Access Control) , answers what are you allowed to do? Mixing the two is a recipe for security bugs:

  • Any logged-in user can hit an admin endpoint.
  • Tenant A can read Tenant B’s records because the code only checked “is logged in”.
  • The UI hides a button, so everyone assumes the backend is safe (it isn’t).

If you’re building a multi-tenant SaaS, a marketplace, or any system with roles, RBAC must live on the server. Front-end checks are just UX niceties; server checks are the real security barrier.

Common RBAC Mistakes

  1. Treating login as authorization , A successful login only proves identity. It says nothing about what the user can access.
  2. Relying on UI hiding , Hiding a button does not stop a determined client from calling the endpoint directly.
  3. Hard-coding role checks in each controller , This leads to duplicated logic and easy slip-ups when new routes are added.
  4. Storing tenant information only in the session , If the tenant ID isn’t verified on every request, a token can be replayed across tenants.

These mistakes surface as the classic bugs listed above and can quickly become a compliance nightmare.

A Practical Server-Side RBAC Model

Here’s the mental model I use for every sensitive request:

  1. Authenticate once , Use whatever fits your stack: session cookies, JWTs, Supabase Auth, etc.
  2. Resolve role + tenant on each request , Pull the user’s role and tenant ID from the token or a quick DB lookup. Never trust a stale value.
  3. Filter data at the query level , Add WHERE tenant_id = :tenantId (or the equivalent) directly in the SQL/ORM query. Do not filter in the UI component after the fact.
  4. Deny by default , If the role isn’t explicitly allowed, return 403 Forbidden. Don’t hope the front-end didn’t call it.

Implementing the Checks

  • Middleware , Place a middleware that extracts the user, role, and tenant, then attaches them to the request context.
  • Policy layer , Create a small policy module (e.g., canReadProject(user, project)) that the route handlers call. This keeps the logic centralized.
  • Database constraints , Where possible, add row-level security policies (PostgreSQL RLS) so even a rogue query can’t bypass tenant filters.

By keeping the RBAC logic out of individual controllers and in a dedicated layer, you reduce surface area for bugs and make future audits straightforward.

Building API Sentinel , A Real-World Example

While studying for my Final Year Project at Iqra University, my team and I built API Sentinel, an AI-powered REST API security testing platform. The idea came from the same frustration: login screens give a false sense of security. We needed a way to prove that APIs enforce RBAC correctly.

  • Upload a Swagger/OpenAPI spec , The platform parses every endpoint, extracts required scopes, and builds a risk profile.
  • AI-driven risk engine , It scores each route based on exposure, required role, and data sensitivity.
  • Automated attack payloads , In isolated Docker containers we fire crafted requests that try to bypass RBAC checks.
  • Structured vulnerability report , The output tells you exactly which endpoints are over-exposed and why.

I pitched the concept and owned most of the Flask backend, the Next.js front-end, the Swagger parser, the risk engine, Docker integration, and the overall architecture. Working with Haris, Eqan, and Saif under Miss Hira Farman’s supervision gave me a sandbox that mirrors real product work.

The project reinforced two lessons:

  1. Login ≠ security , You can’t rely on a front-end login form to protect your API.
  2. Testing RBAC is non-negotiable , Automated tests that try to break role boundaries catch bugs before they hit production.

Implementing RBAC in Your Stack

Whether you’re on Node, Python, Go, or any other language, the steps stay the same:

  • Define roles clearly , List every role and the actions it can perform. Keep the list small; the more granular you get, the harder it is to maintain.
  • Store tenant association , Every user record should have a tenant_id (or equivalent) column.
  • Centralize checks , Use middleware or a policy service that all routes go through.
  • Write integration tests , Simulate requests from users with different roles and tenants. Expect 403 for disallowed actions.
  • Monitor in production , Log denied attempts with user ID, role, and endpoint. Unexpected patterns may indicate a misconfiguration.

By following this checklist you’ll move from “auth” to true RBAC, dramatically reducing the risk of accidental data leaks.

If you need help tightening RBAC, building API security tooling, or any full-stack work, feel free to reach out , see wabby.dev (https://wabby.dev).

Written by Abdul Wahab

← All posts