Skip to main content

Core Concepts

Laravel Permission is built around three fundamental concepts that work together to provide flexible authorization:

Permissions

Individual abilities that can be granted to users (e.g., ‘edit articles’)

Roles

Groups of permissions that can be assigned to users (e.g., ‘editor’ role)

Guards

Authentication contexts that separate permissions (e.g., ‘web’ vs ‘api’)

Permissions

A permission is a single ability that represents an action a user can perform in your application.

What is a Permission?

A permission is the most granular level of access control. It represents a specific action like “edit articles” or “delete users”.
Permissions are stored in the database using the Permission model and have two main attributes:
  • name: The permission identifier (e.g., ‘edit articles’)
  • guard_name: The authentication guard this permission belongs to (e.g., ‘web’)

Creating Permissions

You can create permissions in several ways:
use Spatie\Permission\Models\Permission;

// Create a single permission
Permission::create(['name' => 'edit articles']);

// Create with explicit guard
Permission::create([
    'name' => 'edit articles',
    'guard_name' => 'api'
]);

Finding Permissions

// Find by name
$permission = Permission::findByName('edit articles');
$permission = Permission::findByName('edit articles', 'api');

// Find by ID
$permission = Permission::findById(1);
$permission = Permission::findById(1, 'web');

Permission Naming Conventions

Use descriptive, action-based names for permissions. Common patterns include:
  • Resource + Action: articles.create, articles.edit, articles.delete
  • Verb + Noun: create articles, edit articles, delete articles
  • Simple actions: publish, moderate, administrate

Roles

A role is a named group of permissions that can be assigned to users for easier permission management.

What is a Role?

Roles group related permissions together. Instead of granting 10 individual permissions to a user, you can create an “Editor” role with those permissions and assign the role.
Roles have the same basic structure as permissions:
  • name: The role identifier (e.g., ‘editor’, ‘admin’)
  • guard_name: The authentication guard this role belongs to

Creating Roles

use Spatie\Permission\Models\Role;

// Create a role
$role = Role::create(['name' => 'writer']);
$role = Role::create(['name' => 'editor', 'guard_name' => 'api']);

Finding Roles

// Find by name
$role = Role::findByName('editor');
$role = Role::findByName('editor', 'api');

// Find by ID
$role = Role::findById(1);
$role = Role::findById(1, 'web');

Role Hierarchies

Laravel Permission does not implement role hierarchies by default. Each role is independent and must explicitly have the permissions it needs.
If you need role hierarchies (e.g., Admin inheriting Editor permissions), you can implement them yourself:
// Create base roles
$writer = Role::create(['name' => 'writer']);
$writer->givePermissionTo(['edit articles', 'delete articles']);

$editor = Role::create(['name' => 'editor']);
// Editor gets writer permissions plus additional ones
$editor->givePermissionTo([
    'edit articles',
    'delete articles',
    'publish articles',
    'unpublish articles'
]);

$admin = Role::create(['name' => 'admin']);
// Admin gets all permissions
$admin->givePermissionTo(Permission::all());

Guards

Guards represent different authentication contexts in your application, allowing you to have separate permission systems for different user types.

What is a Guard?

Guards are Laravel’s way of defining different authentication systems. The web guard typically uses session-based authentication, while the api guard might use token-based authentication.
Common use cases for multiple guards:
  • Web vs API: Different permissions for web users and API consumers
  • Admin Panel: Separate admin authentication with different permissions
  • Multi-tenant: Different permission sets per tenant type

Default Guard

The default guard is determined from your config/auth.php file:
config/auth.php
'defaults' => [
    'guard' => 'web',
],
When you create permissions or roles without specifying a guard, they use the default guard.

Working with Multiple Guards

// Web guard (default)
$webPermission = Permission::create(['name' => 'edit articles']);
$webRole = Role::create(['name' => 'editor']);

// API guard
$apiPermission = Permission::create([
    'name' => 'edit articles',
    'guard_name' => 'api'
]);

$apiRole = Role::create([
    'name' => 'editor',
    'guard_name' => 'api'
]);
You cannot assign a permission from one guard to a user authenticated with a different guard. Laravel Permission will throw a GuardDoesNotMatch exception.

Custom Guards

You can create custom guards in config/auth.php:
config/auth.php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],
    
    'admin' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
],
Then create permissions and roles for that guard:
Permission::create(['name' => 'manage users', 'guard_name' => 'admin']);
$adminRole = Role::create(['name' => 'super-admin', 'guard_name' => 'admin']);

Relationship Between Concepts

Understanding how these concepts relate is crucial:

Direct vs Role-Based Permissions

Users can receive permissions in two ways:

Direct Permissions

Permissions assigned directly to the user
$user->givePermissionTo('edit articles');

Role-Based Permissions

Permissions inherited from assigned roles
$role = Role::create(['name' => 'editor']);
$role->givePermissionTo('edit articles');
$user->assignRole('editor');
When checking if a user has a permission, Laravel Permission checks both direct permissions and permissions from roles.

Permission Check Flow

Here’s how permission checks work:
1

Check Direct Permissions

First, check if the user has the permission directly assigned via givePermissionTo().
2

Check Role Permissions

If not found, check if any of the user’s roles have that permission.
3

Return Result

Return true if found in either source, false otherwise.
// User has direct permission
$user->givePermissionTo('edit articles');
$user->can('edit articles'); // true

// User has permission via role
$role = Role::create(['name' => 'editor']);
$role->givePermissionTo('publish articles');
$user->assignRole('editor');
$user->can('publish articles'); // true

// User has no access
$user->can('delete users'); // false

Best Practices

Group related permissions into roles instead of assigning many individual permissions to users:
// ✅ Good: Use roles
$editor = Role::create(['name' => 'editor']);
$editor->givePermissionTo(['edit', 'publish', 'delete']);
$user->assignRole('editor');

// ❌ Avoid: Individual permissions
$user->givePermissionTo(['edit', 'publish', 'delete', ...]);
Use direct permissions for special cases or temporary access:
// User is a writer but needs temporary publish access
$user->assignRole('writer');
$user->givePermissionTo('publish articles');
Use a consistent naming convention throughout your application:
// Choose one style and stick with it
// Dot notation
'articles.create', 'articles.edit', 'articles.delete'

// Or space notation
'create articles', 'edit articles', 'delete articles'
Create all your permissions and roles in a seeder that runs during deployment:
class PermissionSeeder extends Seeder
{
    public function run(): void
    {
        // Clear cache
        app()[PermissionRegistrar::class]->forgetCachedPermissions();
        
        // Create all permissions
        // Create all roles
        // Assign permissions to roles
    }
}
Leverage Laravel’s authorization features alongside this package:
// In a Policy
public function update(User $user, Article $article)
{
    return $user->can('edit articles') && 
           $user->id === $article->author_id;
}

Next Steps

Now that you understand the core concepts:

Using Permissions

Learn how to assign and check permissions in your application

Blade Directives

Use permissions in your views

Build docs developers (and LLMs) love