Authentication and Role Permissions in Laravel with Spatie

Almost every real application needs to distinguish who can do what: an admin can delete users, an editor can write articles, a regular member can only read. Building this system fr...

Authentication and Role Permissions in Laravel with Spatie

Almost every real application needs to distinguish who can do what: an admin can delete users, an editor can write articles, a regular member can only read. Building this system from scratch is complex and bug-prone. The spatie/laravel-permission package is the de facto standard in the Laravel ecosystem for managing roles and permissions cleanly. This tutorial walks you from installation to route protection.

1. Installing the Package

Add the package via Composer, publish the config and migrations, then migrate:

composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate

These migrations create five tables: roles, permissions, model_has_roles, model_has_permissions, and role_has_permissions. Afterwards, clear the config cache:

php artisan optimize:clear

2. Adding the HasRoles Trait to the User Model

So the User model can hold roles and permissions, add the HasRoles trait:

use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}

The trait provides many practical methods such as assignRole(), hasRole(), givePermissionTo(), and can(). All relationships between users, roles, and permissions are stored automatically in the pivot tables created during migration, so you never write raw queries. The core concept is simple: a permission is the smallest grant for a single action, a role is a named collection of permissions, and a user is given roles and therefore inherits all the permissions inside them. Understanding this hierarchy makes it far easier to design a clean access structure from the start.

3. Creating Roles and Permissions

You can create them via tinker or, better, inside a seeder. Create the permissions first, then the roles:

use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

Permission::create(['name' => 'edit articles']);
Permission::create(['name' => 'delete articles']);
Permission::create(['name' => 'publish articles']);

$editor = Role::create(['name' => 'editor']);
$admin  = Role::create(['name' => 'admin']);

Important: name permissions consistently and descriptively (verb + object), because these names are what you will check throughout the app.

4. Assigning Permissions to Roles

Attach permissions to roles. The editor may edit and publish; the admin gets all permissions:

$editor->givePermissionTo(['edit articles', 'publish articles']);

// Admin gets every permission at once
$admin->givePermissionTo(Permission::all());

5. Assigning Roles to Users

Once roles are ready, assign them to a user. This can be one or several roles at once:

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

// Multiple roles
$user->assignRole(['editor', 'author']);

Because permissions are inherited from roles, an admin user automatically has all permissions without assigning them one by one. You can also grant a permission directly to a specific user without a role using givePermissionTo() on the user object — for instance to give one member special access without changing their role. As a general rule, though, manage access through roles because it is easier to maintain: simply change one role's permissions and every user with that role is affected at once.

6. Checking Roles and Permissions

There are several ways to check access. In a controller or PHP logic:

if ($user->hasRole('admin')) {
    // admin only
}

if ($user->can('edit articles')) {
    // user has this permission (directly or via a role)
}

In Blade, use the @can and @role directives to hide UI elements:

@can('delete articles')
    <button>Delete</button>
@endcan

@role('admin')
    <a href="/admin">Admin Panel</a>
@endrole

7. Protecting Routes with Middleware

The cleanest way to guard access is via middleware on routes. The package provides role, permission, and role_or_permission middleware:

Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::resource('users', UserController::class);
});

Route::post('/articles', [ArticleController::class, 'store'])
    ->middleware('permission:edit articles');

On Laravel 11, register the middleware aliases in bootstrap/app.php so they can be used:

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role'       => \Spatie\Permission\Middleware\RoleMiddleware::class,
        'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
    ]);
})

8. Seeding Roles for Consistency

Never create roles by hand in production. Gather them all in a seeder so every environment stays consistent:

class RolePermissionSeeder extends Seeder
{
    public function run(): void
    {
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        foreach (['edit articles', 'delete articles', 'publish articles'] as $perm) {
            Permission::firstOrCreate(['name' => $perm]);
        }

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

Use firstOrCreate so the seeder is safe to run repeatedly without creating duplicates.

Best Practices

  • Check permissions, not roles, in business logic. Roles can change, but capabilities (permissions) are more stable.
  • Clear the permission cache after changing data with php artisan permission:cache-reset.
  • Grant a super-admin via Gate::before so it always passes every check.

With spatie/laravel-permission, an authorization system that is usually complex becomes concise: define permissions, group them into roles, assign them to users, then guard access with @can and middleware. This pattern scales from small apps to large systems with dozens of roles.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Laravel Spatie Role Permission Keamanan
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now
Tim Support
Online
Isi data dulu untuk mulai chat:
Beri rating & testimoni sebelum menutup:
Live chat by gudangcode.com