Security & Authentication

Implementing Role-Based Access Control (RBAC) in Laravel 12

Learn how to set up Role-Based Access Control (RBAC) in Laravel 12 using gates, policies, and permissions to manage user access securely and efficiently.

Muhammad Waqas

Muhammad Waqas

CEO at CentoSquare

|
03-Nov-2025
8 min read
Implementing Role-Based Access Control (RBAC) in Laravel 12

Introduction

Securing your Laravel application starts with proper access control. Role-Based Access Control (RBAC) ensures users can only access features and data they're authorized to use. Whether you're building a SaaS platform, CMS, or enterprise application, RBAC is essential for maintaining security and organization.

In this comprehensive guide, we'll implement RBAC in Laravel 12 using Spatie's Laravel Permission package — the most popular and battle-tested permission management solution in the Laravel ecosystem. With over 12 million downloads, Spatie Permission simplifies role and permission management while providing powerful features out of the box.

By the end of this tutorial, you'll have a fully functional RBAC system with roles, permissions, middleware protection, and Blade directives for conditional UI rendering.


Why Use Spatie Laravel Permission?

While you could build RBAC from scratch, Spatie's package offers significant advantages:

  • Zero Configuration – Works immediately after installation
  • Eloquent Integration – Seamless relationships with your User model
  • Multiple Guards – Support for web, API, and custom guards
  • Wildcard Permissions – Check permissions using patterns
  • Teams/Multi-tenancy Support – Isolate permissions per team
  • Caching Layer – Optimized performance for permission checks
  • Blade Directives – Clean template syntax for authorization
  • Well Maintained – Regular updates and Laravel 12 compatibility

Installation and Setup

Step 1: Install the Package

Install Spatie Laravel Permission via Composer:

composer require spatie/laravel-permission

Step 2: Publish Configuration and Migrations

Publish the package configuration and migration files:

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

This creates:

  • config/permission.php – Configuration file
  • Migration file for roles and permissions tables

Step 3: Run Migrations

Execute migrations to create the necessary database tables:

php artisan migrate

This creates five tables:

  • roles – Stores role definitions
  • permissions – Stores permission definitions
  • model_has_permissions – Direct user permissions
  • model_has_roles – User-role assignments
  • role_has_permissions – Role-permission assignments

Step 4: Add Trait to User Model

Update your App\Models\User model to include the HasRoles trait:

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // Your existing code...
}

Creating Roles and Permissions

Understanding the Hierarchy

In Spatie's RBAC system:

  • Permissions are specific actions (e.g., edit posts, delete users)
  • Roles are groups of permissions (e.g., Admin, Editor)
  • Users are assigned roles and inherit their permissions

Creating Permissions

Create permissions using Eloquent or the artisan tinker:

use Spatie\Permission\Models\Permission;

Permission::create(['name' => 'create posts']);
Permission::create(['name' => 'edit posts']);
Permission::create(['name' => 'delete posts']);
Permission::create(['name' => 'publish posts']);
Permission::create(['name' => 'manage users']);
Permission::create(['name' => 'manage roles']);
Permission::create(['name' => 'view analytics']);

Creating Roles

Create roles and assign permissions:

use Spatie\Permission\Models\Role;

// Create roles
$adminRole = Role::create(['name' => 'admin']);
$editorRole = Role::create(['name' => 'editor']);
$writerRole = Role::create(['name' => 'writer']);

// Assign all permissions to admin
$adminRole->givePermissionTo(Permission::all());

// Assign specific permissions to editor
$editorRole->givePermissionTo([
    'create posts',
    'edit posts',
    'publish posts',
    'view analytics'
]);

// Assign limited permissions to writer
$writerRole->givePermissionTo([
    'create posts',
    'edit posts'
]);

Seeding Roles and Permissions

Create a seeder for production-ready setup:

php artisan make:seeder RolePermissionSeeder
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class RolePermissionSeeder extends Seeder
{
    public function run(): void
    {
        // Reset cached roles and permissions
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        // Create permissions
        $permissions = [
            'create posts',
            'edit posts',
            'delete posts',
            'publish posts',
            'manage users',
            'manage roles',
            'view analytics',
            'manage settings',
        ];

        foreach ($permissions as $permission) {
            Permission::create(['name' => $permission]);
        }

        // Create Super Admin role
        $superAdmin = Role::create(['name' => 'super-admin']);
        $superAdmin->givePermissionTo(Permission::all());

        // Create Admin role
        $admin = Role::create(['name' => 'admin']);
        $admin->givePermissionTo([
            'create posts',
            'edit posts',
            'delete posts',
            'publish posts',
            'view analytics',
        ]);

        // Create Editor role
        $editor = Role::create(['name' => 'editor']);
        $editor->givePermissionTo([
            'create posts',
            'edit posts',
            'publish posts',
        ]);

        // Create Writer role
        $writer = Role::create(['name' => 'writer']);
        $writer->givePermissionTo([
            'create posts',
            'edit posts',
        ]);
    }
}

Run the seeder:

php artisan db:seed --class=RolePermissionSeeder

Assigning Roles to Users

Assign Roles

Assign single or multiple roles to users:

use App\Models\User;

$user = User::find(1);

// Assign a single role
$user->assignRole('admin');

// Assign multiple roles
$user->assignRole(['editor', 'writer']);

// Assign role using Role model
$user->assignRole(Role::findByName('admin'));

Remove Roles

// Remove a specific role
$user->removeRole('editor');

// Remove all roles
$user->syncRoles([]);

Sync Roles

Replace all existing roles with new ones:

// User will only have 'admin' role
$user->syncRoles(['admin']);

Direct Permission Assignment

Assign permissions directly to users (bypassing roles):

$user->givePermissionTo('manage settings');

// Give multiple permissions
$user->givePermissionTo(['create posts', 'edit posts']);

// Revoke permission
$user->revokePermissionTo('delete posts');

Checking Permissions and Roles

Check User Roles

// Check single role
if ($user->hasRole('admin')) {
    // User is an admin
}

// Check any of multiple roles
if ($user->hasAnyRole(['admin', 'editor'])) {
    // User is either admin or editor
}

// Check all roles
if ($user->hasAllRoles(['admin', 'super-admin'])) {
    // User has both roles
}

Check Permissions

// Check single permission
if ($user->hasPermissionTo('edit posts')) {
    // User can edit posts
}

// Alternative syntax
if ($user->can('edit posts')) {
    // User can edit posts
}

// Check multiple permissions (any)
if ($user->hasAnyPermission(['edit posts', 'delete posts'])) {
    // User has at least one permission
}

// Check multiple permissions (all)
if ($user->hasAllPermissions(['create posts', 'edit posts'])) {
    // User has both permissions
}

Protecting Routes with Middleware

Spatie Permission provides built-in middleware for route protection.

Register Middleware

In Laravel 12, register middleware in bootstrap/app.php:

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
            'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
            'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

Protect Routes by Role

// Single role
Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin/dashboard', [AdminController::class, 'index']);
});

// Multiple roles (OR condition)
Route::middleware(['auth', 'role:admin|editor'])->group(function () {
    Route::get('/posts', [PostController::class, 'index']);
});

Protect Routes by Permission

// Single permission
Route::middleware(['auth', 'permission:edit posts'])->group(function () {
    Route::put('/posts/{post}', [PostController::class, 'update']);
});

// Multiple permissions (OR condition)
Route::middleware(['auth', 'permission:create posts|edit posts'])->group(function () {
    Route::resource('posts', PostController::class);
});

Combined Role and Permission Check

Route::middleware(['auth', 'role_or_permission:admin|edit posts'])->group(function () {
    Route::get('/posts/edit', [PostController::class, 'edit']);
});

Controller Authorization

Using authorize() Method

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function edit(Post $post)
    {
        $this->authorize('edit posts');
        
        return view('posts.edit', compact('post'));
    }

    public function destroy(Post $post)
    {
        $this->authorize('delete posts');
        
        $post->delete();
        
        return redirect()->route('posts.index')
            ->with('success', 'Post deleted successfully');
    }
}

Using Gate Facade

use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post)
{
    if (Gate::denies('edit posts')) {
        abort(403, 'Unauthorized action');
    }

    $post->update($request->validated());
    
    return redirect()->route('posts.show', $post);
}

Blade Directives for Conditional UI

Role-Based Directives

@role('admin')
    <a href="{{ route('admin.dashboard') }}">Admin Dashboard</a>
@endrole

@hasrole('editor')
    <button>Edit Content</button>
@endhasrole

@hasanyrole('admin|editor')
    <div>Admin or Editor Content</div>
@endhasanyrole

Permission-Based Directives

@can('create posts')
    <a href="{{ route('posts.create') }}" class="btn btn-primary">
        Create New Post
    </a>
@endcan

@can('delete posts')
    <form action="{{ route('posts.destroy', $post) }}" method="POST">
        @csrf
        @method('DELETE')
        <button type="submit" class="btn btn-danger">Delete</button>
    </form>
@endcan

@canany(['edit posts', 'delete posts'])
    <div>Post Management Tools</div>
@endcanany

Combined Checks

@role('admin')
    @can('manage users')
        <a href="{{ route('users.index') }}">Manage Users</a>
    @endcan
@endrole

Advanced Features

Super Admin Override

Grant all permissions to super admins automatically in App\Providers\AuthServiceProvider:

use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::before(function ($user, $ability) {
        return $user->hasRole('super-admin') ? true : null;
    });
}

Wildcard Permissions

Check permissions using wildcards:

$user->givePermissionTo('posts.*');

// Check wildcard
if ($user->can('posts.*')) {
    // User can do anything with posts
}

Cache Management

Clear permission cache after updates:

use Spatie\Permission\PermissionRegistrar;

app(PermissionRegistrar::class)->forgetCachedPermissions();

// Or using artisan
php artisan permission:cache-reset

Conclusion

Implementing RBAC in Laravel 12 with Spatie's Laravel Permission package provides enterprise-grade access control with minimal code. The package handles complex permission logic, caching, and guard management automatically, letting you focus on building features.

Key takeaways:

  • Install and configure Spatie Permission in minutes
  • Create granular permissions and flexible roles
  • Protect routes with dedicated middleware
  • Build conditional UIs using Blade directives
  • Scale permissions across teams and multi-tenant applications

Need help implementing RBAC in your Laravel project? NeedLaravelSite specializes in building secure, scalable Laravel applications with proper access control systems. Contact us for expert Laravel development services.


Related Resources:


Article Tags

Laravel RBAC Laravel 12 permissions Spatie Laravel Permission Laravel authorization Laravel roles and permissions Laravel access control Laravel security Laravel middleware Laravel gates and policies Role-Based Access Control Laravel

About the Author

Muhammad Waqas

Muhammad Waqas

CEO at CentoSquare

Founder & CEO at CentoSquare | Creator of NeedLaravelSite | Helping Businesses Grow with Cutting-Edge Web, Mobile & Marketing Solutions | Building Innovative Products