Laravel 13 Roles & Permissions with Spatie - Step by Step

Arlind Musliu cofounder at Lucky Media
Arlind Musliu

February 12, 2025 · 5 min read

Laravel for Beginners Roles and Permissions with Spatie package

2026 UPDATE - LARAVEL 13

We’re excited to announce that we have updated all of our blog post examples to reflect the new Laravel 13 version! Our previous examples were based on Laravel 10, but with the release of Laravel 13, we wanted to ensure that our readers have access to the most up-to-date information and examples.

Roles and Permissions in Laravel 13

Laravel is known for its simplicity and elegance in building modern web apps. One key feature is its roles and permissions system, which helps control user access and keeps your app secure. This guide explores what roles and permissions are, why they matter, and how to use them in Laravel 13.

What Are Roles and Permissions?

Roles

In Laravel, a role is a label given to a user that defines their access level in the app. For example, you might have roles like "admin," "editor," or "user." Each role is linked to a set of permissions that specify what actions the user can perform.

Permissions

Permissions are actions users can perform, such as "manage users" or "edit posts." They help you control exactly what each role can do.

Using roles and permissions is important for keeping your app secure and organized. Here’s why:

  1. Scalability: As your app grows, managing user access individually becomes difficult. Roles and permissions provide a scalable way to handle access control.

  2. Security: By clearly defining what each role can and cannot do, you reduce the risk of unauthorized access to sensitive parts of your app.

  3. Maintainability: A well-defined roles and permissions system makes it easier to update and manage user access as your app evolves.

laravel best framework php

Using Roles and Permissions in Laravel 13

Step 1: Installing the Laravel Authorization Package

Laravel 13 has built-in support for roles and permissions. To make the tutorial simpler, we will use the Spatie Laravel-Permission package. It’s the most used package in Laravel for roles and permissions.

To install the package, run:

composer require spatie/laravel-permission

After installation, publish the migration files and run the migrations:

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

Building a Laravel app that needs roles and permissions?

Lucky Media builds custom backends for startups and enterprises. Let's talk

Step 2: Setting Up Models and Relationships

Next, set up the necessary relationships in your User model. Add the HasRoles trait provided by the Spatie package:

use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // Your existing code...
}

This trait adds methods to assign and check roles and permissions for the user.

Step 3: Creating Roles and Permissions

Now that everything is set up, you can create roles and permissions using Artisan commands or directly in your code. For example, you can create roles and permissions in a seeder with the command we already know:
php artisan make:seeder RolesAndPermissionsSeeder

Now you can modify the RolesAndPermissionsSeeder.php file to match the following:

namespace Database\Seeders;

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

class RolesAndPermissionsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
		// Create roles
        $adminRole = Role::create(['name' => 'admin']);
        $editorRole = Role::create(['name' => 'editor']);
        $userRole = Role::create(['name' => 'user']);

        // Create permissions
        $viewPosts = Permission::create(['name' => 'view posts']);
        $editPosts = Permission::create(['name' => 'edit posts']);
        $deletePosts = Permission::create(['name' => 'delete posts']);
    }
}

Step 4: Assigning Roles and Permissions to Users

We can assign roles and permissions to users using the methods provided by the HasRoles trait:

// Find a user and assign a role
$user = User::find(1);
$user->assignRole('admin');

There are different ways how you can implement this in your app, we believe it’s enough to show how you make the actual connection with the code we just provided.

Let’s modify the seeder we just created to automatically seed some roles to our users:

namespace Database\Seeders;

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

class RolesAndPermissionsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
		// Create roles
        $adminRole = Role::create(['name' => 'admin']);
        $editorRole = Role::create(['name' => 'editor']);
        $userRole = Role::create(['name' => 'user']);

        // Create permissions
        $viewPosts = Permission::create(['name' => 'view posts']);
        $editPosts = Permission::create(['name' => 'edit posts']);
        $deletePosts = Permission::create(['name' => 'delete posts']);

        // Assign permissions to roles
        $adminRole->givePermissionTo([$viewPosts, $editPosts, $deletePosts]);
        $editorRole->givePermissionTo([$viewPosts, $editPosts]);
        $userRole->givePermissionTo($viewPosts);

		// Get all users
        $users = User::all();

        // Assign roles based on the user's position in the list
        foreach ($users as $index => $user) {
			// First three users will be admins
			if ($index < 3) {
                $user->assignRole('admin');
            } 
			// The following three users will be editors
			elseif ($index < 6) {
                $user->assignRole('editor');
            }
 			// The rest will have the role user
			else {
                $user->assignRole('user');
            }
        }
    }
}

You can also check if a user has a specific role or permission. Let’s modify our existing PostController to do the following restrictions:

  • Only admins can delete posts, but not editors and users.

  • Admins and editors can edit articles, but not users.

  • Everyone (users, editors, and admins) will be able to view a single post.

We can do this either by checking the role or by checking the permission of the authenticated user. As for editing posts, we showed 2 ways to do the same thing. To view the edit post page we will check if the user has the role of admin or editor. To update the post we check if the permission of that user has edit posts. If not, we will redirect the user to the post-show page.

namespace App\Http\Controllers;

use App\Events\PostPublished;
use App\Jobs\SendNewPostNotification;
use App\Mail\NewPostPublished;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;

class PostController extends Controller
{
	// Display a single blog post
    public function show($id)
    {
		// Check if user has permission to view post
        if (!auth()->user()->can('view posts')) {
            return redirect()->route('posts.index');
        }

        $post = Post::with('user', 'tags', 'images')
            ->findOrFail($id);

        return view('posts.show', ['post' => $post]);
    }

    // Show a form for editing the specified blog post
    public function edit($id)
    {
        // Check if user has the admin or editor role
        if (!auth()->user()->hasRole(['admin', 'editor'])) {
            return redirect()->route('posts.show', $post);
        }

        $post = Post::findOrFail($id);
        $tags = Tag::all();
        return view('posts.edit', ['post' => $post, 'tags' => $tags]);
    }

    // Update the specified blog post
    public function update(Request $request, Post $post)
    {
        // Check if user has permission to edit posts
        if (!auth()->user()->can('edit posts')) {
            return redirect()->route('posts.show', $post);
        }

        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
            // other validation rules...
        ]);

        $tags = $request->validate([
            'tags' => 'required',
        ]);

        $post->update($validatedData);

        // we modify the tags of the post by using the sync() method
        $post->tags()->sync($tags);

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

    // Delete a blog post
    public function destroy(Post $post)
    {
		// Check if user is an admin
        if (!auth()->user()->hasRole('admin'])) {
            return redirect()->route('posts.show', $post);
        }

        $post->delete();

        return redirect()->route('posts.index');
    }
}
  • To do it by checking the role of the authenticated user we should use:

// Check if user is an admin
if (!auth()->user()->hasRole('admin')) {
	return redirect()->route('posts.show', $post);
}
  • To do it by checking the permission of the authenticated user we should use:

// Check if user has permission to edit posts
if (!auth()->user()->can('edit posts')) {
	return redirect()->route('posts.show', $post);
}

Step 5: Protecting Routes with Roles and Permissions

Laravel’s middleware makes it easy to protect routes based on roles and permissions. You can use the role and permission middleware provided by the Spatie package. This will help you protect individual routes or you can also group routes with the same protection rules.

// We need to use this example in the blog post example that we have
Route::group(['middleware' => ['role:admin']], function () {
    Route::get('/admin', function () {
        // Only accessible by admins
    });
});

Route::group(['middleware' => ['permission:edit posts']], function () {
    Route::get('/posts/{post}', function () {
        // Only accessible by users with 'edit posts' permission
    });
});
Using Laravel Pipelines

Conclusion

Roles and permissions are important for secure user access in Laravel apps. With Laravel 13 and the Spatie package, you can easily manage access and build a secure, scalable app. By following the steps in this guide, you’ll be on your way to building a secure and scalable app.

Upcoming Articles in the Series

  1. Laravel for Beginners: Differences of using Policies vs Roles and Permissions

  2. Laravel for Beginners: Using Laravel Spark with Stripe and Paddle

This article is part of our series Laravel for Beginners: A Step-by-Step Guide to Learn the Concepts.


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let’s get in touch.

Lucky Media is proud to be recognized as a leading Laravel Development Agency

Technologies

Laravel
Arlind Musliu cofounder at Lucky Media
Arlind Musliu

Cofounder and CFO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork