spatie/laravel-authorize
Route middleware for Laravel authorization. Protect routes and groups using Laravel’s Gate abilities via the can: middleware syntax, with support for route model binding (e.g., can:editPost,post) to authorize access to specific models.
Installation:
composer require spatie/laravel-authorize
No additional configuration is required—it leverages Laravel’s built-in can() authorization.
First Use Case: Protect a route by adding the middleware to its definition:
Route::get('/admin/dashboard', [
'middleware' => 'can:access-admin-dashboard',
'uses' => 'AdminController@dashboard',
]);
Ensure your User model (or relevant model) implements Laravel’s Authorizable contract (e.g., via Spatie\Permission\Traits\HasRoles).
Define Policies:
Create a policy for the model/resource you’re protecting (e.g., app/Policies/PostPolicy.php):
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
public function viewTopSecretPage(User $user)
{
return $user->isAdmin(); // Custom logic
}
}
Register the policy in AuthServiceProvider:
protected $policies = [
Post::class => PostPolicy::class,
];
Test Authorization: Use Tinker or tests to verify:
php artisan tinker
>>> $user = App\Models\User::first();
>>> $user->can('access-admin-dashboard'); // Returns bool
Route-Level Protection:
Controller-Level Checks:
authorize() helper or can() method in controllers:
public function edit(Post $post)
{
$this->authorize('update', $post); // Policy-based
// OR
if (!auth()->user()->can('edit-post')) {
abort(403);
}
}
Gate definitions for dynamic checks:
Gate::define('edit-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
Policy Composition:
// PostPolicy.php
public function delete(User $user, Post $post)
{
return $user->isSuperAdmin() || $post->user_id === $user->id;
}
$this->authorize('delete', $post);
Middleware Groups:
Route::middleware(['can:manage-users'])->group(function () {
Route::resource('users', UserController::class);
});
API Resource Protection:
can middleware:
Route::get('/api/posts/{post}', function (Post $post) {
return $this->authorize('view', $post) ? $post : abort(403);
})->middleware('can:view-post');
@can('access-admin-dashboard')
<a href="/admin">Admin Panel</a>
@endcan
Event::listen('authorized:can:viewTopSecretPage', function ($user) {
Log::info("User {$user->id} accessed top-secret page.");
});
Policy Registration:
AuthServiceProvider causes Gate not defined errors.$policies array or use Gate::define() for ad-hoc rules.Middleware Caching:
php artisan optimize). Clear cache after policy changes:
php artisan cache:clear
php artisan route:clear
Model Binding Conflicts:
{post}) may interfere with policy resolution if the model lacks a resolveRouteBinding method.Route::get('/posts/{post}', function (Post $post) {
$this->authorize('view', $post);
})->middleware('can:view-post,{post}'); // Pass model via middleware params
Permission Logic Errors:
Deprecated Laravel Features:
Gate vs. Policy changes).APP_DEBUG=true in .env to see authorization failure messages (e.g., User does not have permission to perform action).authorizing event:
Event::listen('authorizing', function ($user, $ability) {
Log::debug("Authorization check for {$ability} by user {$user->id}");
});
public function test_user_can_access_admin_dashboard()
{
$user = User::factory()->admin()->create();
$this->assertTrue($user->can('access-admin-dashboard'));
}
Custom Middleware Parameters: Extend the middleware to accept additional parameters:
// app/Http/Middleware/AuthorizeWithParams.php
public function handle($request, Closure $next, $ability, $model = null)
{
if (!$request->user()->can($ability, $model)) {
abort(403);
}
return $next($request);
}
Register in app/Http/Kernel.php:
'can.param' => \App\Http\Middleware\AuthorizeWithParams::class,
Use in routes:
Route::get('/posts/{post}', [
'middleware' => ['can.param:view-post,{post}'],
'uses' => 'PostController@show',
]);
Policy Caching:
Cache policy results for performance (e.g., using Spatie\CacheablePolicy):
use Spatie\CacheablePolicy\CacheablePolicy;
class PostPolicy
{
use CacheablePolicy;
public function view(User $user, Post $post)
{
return $user->isAdmin();
}
}
Role-Based Middleware: Create a role-specific middleware:
// app/Http/Middleware/RoleMiddleware.php
public function handle($request, Closure $next, $role)
{
if (!auth()->user()->hasRole($role)) {
abort(403);
}
return $next($request);
}
Use with spatie/laravel-permission:
Route::middleware(['role:admin'])->group(...);
Fallback Policies: Define fallback policies for unregistered gates:
Gate::before(function (User $user) {
if ($user->isSuperAdmin()) {
return true; // Allow all actions
}
});
How can I help you explore Laravel packages today?