Back to Blog
Blog/Architecture

Building Granular RBAC & Cascading Subscription Feature Gates in Multi-User SaaS

How to implement granular sub-user permissions, enforce tenant-wide subscription lockouts, and turn team collaboration into a paid SaaS add-on.

Akram Hafaiedh
Akram Hafaiedh
Full Stack Developer
2026-03-065 min read
// SYSTEM ARCHITECTURE PATTERN
PRODUCTION VERIFIED
TECHNICAL CASE STUDY

Building Granular RBAC & Cascading Subscription Feature Gates in Multi-User SaaS

LARAVELREDISMULTI-TENANT

Granular RBAC & Tenant Subscription Feature Gates

In B2B SaaS platforms (like ERP or billing systems), workspace owners frequently invite sub-users (referred to as Collaborators) to manage day-to-day operations. However, delegating access presents two critical engineering challenges:

1. Granular Permission Scoping: Collaborators must not have blanket admin privileges. A warehouse manager might create inventory items but must be restricted from deleting invoices or editing tax settings.

2. Cascading Tenant Lockouts: When a primary account's subscription expires, document creation (sales, expenses, invoices) must be instantly blocked across all linked collaborators, preventing sub-users from bypassing billing expiration checks.

Here is how we designed a secure Role-Based Access Control (RBAC) pipeline and turned the "Collaborator Management" module into a revenue-generating subscription add-on.


1. Designing Granular Module Permissions

Instead of assigning rigid global roles (admin, staff), create a granular permission schema mapping specific actions (invoices.create, invoices.delete, expenses.view) to custom roles defined by the account owner.

php
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// app/Services/PermissionService.php
namespace App\Services;

use App\Models\User;

class PermissionService
{
    public static function canUserPerform(User $user, string $permission): bool
    {
        class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// 1. Primary workspace owners bypass granular checks
        if ($user->is_workspace_owner) {
            return true;
        }

        class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// 2. Fetch collaborator role permissions
        $permissions = $user->role ? $user->role->permissions->pluck('name')->toArray() : [];

        return in_array($permission, $permissions);
    }
}

You can then enforce these rules cleanly inside Laravel Middleware or Policy definitions:

php
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// app/Http/Middleware/CheckModulePermission.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use App\Services\PermissionService;

class CheckModulePermission
{
    public function handle(Request $request, Closure $next, string $permission)
    {
        $user = $request->user();

        if (!PermissionService::canUserPerform($user, $permission)) {
            return response()->json([
                'error' => 'You do not have permission to execute this action.'
            ], 403);
        }

        return $next($request);
    }
}

2. Cascading Subscription Lockouts to Collaborators

A common security vulnerability occurs when subscription expiration checks only target the currently logged-in user. If a sub-user logs in, their individual account status might appear active, allowing them to create sales and expense documents even after the workspace owner's subscription has expired.

To fix this, resolve the primary tenant's subscription status during every document creation request:

php
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// app/Http/Middleware/EnsureActiveSubscription.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureActiveSubscription
{
    public function handle(Request $request, Closure $next)
    {
        $user = $request->user();
        
        class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// Resolve the workspace owner (primary tenant)
        $owner = $user->is_workspace_owner ? $user : $user->workspaceOwner;

        if (!$owner || !$owner->hasActiveSubscription()) {
            return response()->json([
                'error' => 'Subscription expired. Please ask your account administrator to extend your plan.',
                'code' => 'SUBSCRIPTION_EXPIRED'
            ], 402);
        }

        return $next($request);
    }
}

3. Monetizing Collaborator Access as a Paid SaaS Add-On

After building a reliable RBAC system and securing sub-user access controls, the team recognized that multi-user collaboration was a key differentiator for growing businesses.

Instead of bundling collaborator management into all base plans, we gated the feature behind a paid subscription add-on:

php
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// app/Models/Workspace.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Workspace extends Model
{
    public function canAddCollaborators(): bool
    {
        class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// Check if tenant has purchased the Collaborators Add-On or has Enterprise Tier
        return $this->hasAddon('collaborators_module') || $this->plan_tier === 'enterprise';
    }

    public function maxCollaboratorLimit(): int
    {
        if (!$this->canAddCollaborators()) {
            return 0; class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// Free / Standard plan without add-on
        }

        return $this->addon_seats ?? 5;
    }
}

By combining granular permission scoping, tenant-wide subscription enforcement, and feature gating, we turned an internal user management feature into a secure, revenue-generating SaaS module.

Engineering Collaboration

Building a High-Scale SaaS or Complex System?

Let’s discuss your architecture, full-stack development, or system modernization needs.

Related Articles

// BACKEND & PIPELINE ENGINEERING
PRODUCTION VERIFIED
TECHNICAL CASE STUDY

Eliminating N+1 Queries & Indexing Multi-Tenant Tables: Optimizing Slow Database Queries under Heavy SaaS Load

PHP 8.3QUEUESPARALLEL WORKERS
Backend
2026-03-30•Backend

Eliminating N+1 Queries & Indexing Multi-Tenant Tables: Optimizing Slow Database Queries under Heavy SaaS Load

Read Article
// BACKEND & PIPELINE ENGINEERING
PRODUCTION VERIFIED
TECHNICAL CASE STUDY

Streaming Large Financial Exports: Preventing Memory Overflows in Laravel & Vue 3

PHP 8.3QUEUESPARALLEL WORKERS
Backend
2026-03-25•Backend

Streaming Large Financial Exports: Preventing Memory Overflows in Laravel & Vue 3

Read Article