Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon What are the best practices for using Spatie middleware in a Laravel application


What are the best practices for using Spatie middleware in a Laravel application


Best practices for using Spatie middleware in a Laravel application focus on proper registration, usage, and structuring of role and permission checks to ensure secure and maintainable authorization. Here are detailed guidelines based on Spatie's documentation and community insights:

1. Register Middleware Aliases Properly

- In Laravel 9 and 10, register the middleware aliases in `app/Http/Kernel.php` under the `$middlewareAliases` property:

php
protected $middlewareAliases = [
    'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
];

- In Laravel 11, middleware aliases are registered in `/bootstrap/app.php` using the `withMiddleware()` method.

This setup allows you to use short aliases like `'role'` or `'permission'` in your routes and controllers for cleaner code[1].

2. Use Middleware in Routes and Controllers for Authorization

- Apply middleware directly in route groups or individual routes to restrict access based on roles or permissions:

php
Route::group(['middleware' => ['role:manager']], function () {
    // routes for managers only
});

Route::group(['middleware' => ['permission:publish articles']], function () {
    // routes for users with publish articles permission
});

- You can specify multiple roles or permissions separated by a pipe `|` (OR logic):

php
Route::group(['middleware' => ['role:manager|writer']], function () {
    // routes accessible if user has either manager OR writer role
});

- For controllers, in Laravel 10 and earlier, apply middleware in the constructor:

php
public function __construct()
{
    $this->middleware(['role:manager', 'permission:publish articles|edit articles']);
}

- In Laravel 11, if the controller implements `HasMiddleware` interface, use the static `middleware()` method to register middleware with fine-grained control over which methods they apply to[1].

3. Prefer Permission Checks Over Roles for Flexibility

- Spatie recommends focusing on permissions rather than roles for authorization checks because permissions provide more granular control.

- Assign permissions to roles, then assign roles to users. This way, you check permissions in middleware or policies, which is more flexible and maintainable than checking roles directly[3][6].

- For example, grant all necessary permissions to an "admin" role instead of checking for the role explicitly in middleware. Then use permission middleware only:

php
// Assign all permissions to admin role
$permissions = \Spatie\Permission\Models\Permission::all();
$role = \Spatie\Permission\Models\Role::findByName('admin');
$role->syncPermissions($permissions);

// Middleware usage
Route::middleware(['permission:seasons show active'])->group(function () {
    // ...
});

This avoids issues where a user with a permission but not the role gets denied access unexpectedly[6].

4. Handle Middleware Priority to Avoid Unexpected 404s

- If your app returns 404 Not Found instead of 403 Forbidden when permission checks fail, it may be due to middleware priority clashes.

- Ensure Spatie's permission middleware runs before Laravel's `SubstituteBindings` middleware by adjusting middleware priority in `app/Http/Kernel.php` or using Laravel 11's `$middleware->prependToGroup()` method[1].

5. Use Middleware Static Methods for Dynamic Middleware

- Spatie middleware classes provide static `using()` methods to apply middleware with dynamic parameters, which can be useful for complex scenarios:

php
Route::group(['middleware' => [\Spatie\Permission\Middleware\PermissionMiddleware::using('publish articles|edit articles')]], function () {
    // ...
});

This approach is supported from Laravel 10.9+ and Laravel 11, allowing more expressive middleware application[1].

6. Combine Middleware with Laravel Policies for Method-Level Authorization

- Middleware is good for route-level access control, but for fine-grained method-level authorization (e.g., differentiate between create, update, delete actions), use Laravel's policy classes.

- Define policies that check specific permissions for each action, and call `$this->authorize()` or use `authorizeResource()` in controllers.

- This complements middleware by providing detailed control over what a user can do within a resource, beyond just route access[4].

7. Pass Permissions to Views for Conditional UI Elements

- To show or hide UI elements based on permissions, pass the authenticated user's permissions to your frontend (e.g., via middleware like `HandleInertiaRequests`).

- Then conditionally render links or buttons in your views depending on the user's permissions, improving UX and security by not exposing unauthorized options[8].

Following these best practices ensures that your Laravel application using Spatie's roles and permissions middleware is secure, efficient, and maintainable. Use middleware aliases properly, prefer permission checks over roles, manage middleware priority, combine with policies for detailed authorization, and leverage dynamic middleware methods as needed.

Citations:
[1] https://spatie.be/docs/laravel-permission/v6/basic-usage/middleware
[2] https://laracasts.com/discuss/channels/laravel/best-way-to-use-spatie-permissions-in-controller-or-routes
[3] https://github.com/spatie/laravel-permission/blob/main/docs/best-practices/roles-vs-permissions.md
[4] https://stackoverflow.com/questions/66920162/how-to-use-spatie-permission-middleware-in-laravel
[5] https://spatie.be/docs/laravel-permission/v6/best-practices/performance
[6] https://stackoverflow.com/questions/56617848/laravel-spatie-middleware-using-role-or-permission
[7] https://www.reddit.com/r/laravel/comments/b3qu57/begginer_question_best_practice_for_different/
[8] https://www.youtube.com/watch?v=3hSBJCVwh78