Skip to content

Secure Your Laravel 12 App: Roles & Permissions with Spatie Explained

Learn how to implement user roles and permissions in Laravel 12 using the Spatie package. A complete guide to RBAC, role management, and secure access control.

Dasun Kalupahana 2 min read 1.4K views 0 comments

Role-based access control (RBAC) is essential for most web applications. In this tutorial, I’ll show you how to implement a robust permission system in Laravel 11 using the popular Spatie Laravel-Permission package.

Why Spatie Permissions?

The Spatie Laravel-Permission package provides:

  • Simple role and permission management
  • Middleware protection for routes
  • Blade directives for view protection
  • Database caching for optimal performance
  • Clean, maintainable code structure

Step 1: Install Spatie Laravel-Permission your project

To install the Spatie Laravel Permission (ACL) package, you’ll need to run the following commands in your terminal inside your Laravel project:

composer require spatie/laravel-permission

Once you install the Spatie Laravel Permission package with the version you need, you can publish its config and migration files so you can customize them. Run the following command in your terminal:

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

After publishing, you’ll see the config/permission.php file and the migration file inside database/migrations/.
Now, to create the necessary tables for roles and permissions, just run:

php artisan migrate

Step 2: Set Up the User Model

To enable role & permission management on your User model, you need to add the HasRoles trait from Spatie.

Open your app/Models/User.php file and update it like this:

<?php

namespace App\Models;

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

class User extends Authenticatable
{
    use HasRoles;

    // ... rest of your model code
}

You just need to alias them in bootstrap/app.php (since Laravel 11+ doesn’t use the old Kernel.php for middleware registration).

<?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();

3. Create Roles & Permissions (Seeder Example)
<?php

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

class RolePermissionSeeder extends Seeder
{
    public function run(): void
    {
        Permission::create(['name' => 'view articles']);
        Permission::create(['name' => 'create articles']);
        Permission::create(['name' => 'edit articles']);
        Permission::create(['name' => 'delete articles']);

        // Create roles and assign permissions
        $writer = Role::create(['name' => 'writer']);
        $writer->givePermissionTo(['view articles', 'create articles', 'edit articles']);

        $admin = Role::create(['name' => 'admin']);
        $admin->givePermissionTo(Permission::all());
    }
}

Run seeder:


php artisan db:seed --class=RolePermissionSeeder

4. Assign Roles to Users

$user = User::find(1);
$user->assignRole('admin');

$writer = User::find(2);
$writer->assignRole('writer');

5. Protect Routes with Middleware
Route::middleware(['role:admin'])->group(function () {
    Route::get('/admin/dashboard', fn() => 'Admin Dashboard');
});

Route::get('/articles/create', fn() => 'Create Article Page')
    ->middleware('permission:create articles');

6. Blade Protection With can function

Because all permissions will be registered on Laravel’s gate, you can check if a user has a permission with Laravel’s default can function:

@can('edit articles')
    <button>Edit</button>
@endcan

7. FormRequest Authorization (Best Practice)

Instead of checking permissions in controllers, use FormRequest classes.

<?php
 
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateArticleRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Only allow if user has "create articles" permission
        return $this->user()?->can('create articles');
    }

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
    }
}

Example: UpdateArticleRequest

<?php 

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateArticleRequest extends FormRequest
{
    public function authorize(): bool
    {
        // Allow only if user can "edit articles"
        return $this->user()?->can('edit articles');
    }

    public function rules(): array
    {
        return [
            'title' => 'sometimes|required|string|max:255',
            'content' => 'sometimes|required|string',
        ];
    }
}

Using in Controller

<?php

namespace App\Http\Controllers;

use App\Models\Article;
use App\Http\Requests\CreateArticleRequest;
use App\Http\Requests\UpdateArticleRequest;

class ArticleController extends Controller
{
    public function store(CreateArticleRequest $request)
    {
        Article::create($request->validated());
        return back()->with('success', 'Article created!');
    }

    public function update(UpdateArticleRequest $request, Article $article)
    {
        $article->update($request->validated());
        return back()->with('success', 'Article updated!');
    }
}

If the user does not have the required permission, Laravel automatically throws 403 Unauthorized before entering the controller.

Conclusion

Spatie’s Laravel Permission package gives you a clean and flexible way to handle role-based access control (RBAC) in your application. By combining middleware, Blade directives, and FormRequest authorization, you can protect your routes, controllers, and views with minimal effort.

  • Middleware ensures routes are secure.
  • Blade directives ensure the UI only shows what’s allowed.
  • FormRequests keep controllers clean while enforcing permissions automatically.

With this setup, your app follows best practices in authorization and stays scalable as your roles and permissions grow. Whether you’re building a small blog or a large enterprise system, this approach ensures your application remains secure, maintainable, and easy to extend.

If you found this helpful, feel free to share or drop a comment. Happy coding with Laravel! 🧱✨

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Free download: Laravel interview questions (PDF)

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.