Laravel Tutorial · Beyond the Basics · Lesson 21 of 27
Authorization: Gates and Policies
Decide what a signed-in user may do with gates for simple checks and policies for model rules, then enforce them in routes, controllers and Blade.
Authentication vs authorization
Authentication answers who are you?; authorization answers what may you do?
- Gates are closures for app-wide abilities:
Gate::define('view-reports', fn (User $u) => $u->is_admin). - Policies group rules for one model:
php artisan make:policy PostPolicy --model=Postwith methods likeupdate(User $user, Post $post).
Check abilities with Gate::allows('view-reports'), $user->can('update', $post), Gate::authorize(...) (throws 403), the can middleware, or @can in Blade. A before hook can let admins do everything.
<?php
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
class Post extends Model
{
protected $guarded = [];
}
class PostPolicy
{
public function before(User $user): ?bool
{
return $user->email === '[email protected]' ? true : null; // admins can do anything
}
public function update(User $user, Post $post): bool
{
return $post->user_id === $user->id; // only the author
}
}
Gate::policy(Post::class, PostPolicy::class);
Gate::define('view-reports', fn (User $user) => str_ends_with($user->email, '@example.com'));
$ana = (new User)->forceFill(['id' => 1, 'name' => 'Ana', 'email' => '[email protected]']);
$ben = (new User)->forceFill(['id' => 2, 'name' => 'Ben', 'email' => '[email protected]']);
$admin = (new User)->forceFill(['id' => 3, 'name' => 'Root', 'email' => '[email protected]']);
$post = new Post(['id' => 10, 'user_id' => 1, 'title' => "Ana's post"]);
foreach ([$ana, $ben, $admin] as $user) {
echo str_pad($user->name, 5), ' update post: ', $user->can('update', $post) ? 'yes' : 'no',
' | view reports: ', Gate::forUser($user)->allows('view-reports') ? 'yes' : 'no', "\n";
}
Route::get('/reports', function () {
Gate::authorize('view-reports'); // throws a 403 if not allowed
return 'Quarterly numbers';
});
Auth::setUser($ben);
visit('GET', '/reports');
Auth::setUser($ana);
visit('GET', '/reports');
Output
Ana update post: yes | view reports: yes
Ben update post: no | view reports: no
Root update post: yes | view reports: yes
GET /reports -> 403 {
"message": "This action is unauthorized."
}
GET /reports -> 200 Quarterly numbersRun this on your computer — see “Get Started” for setup.
Test yourself
What does Gate::authorize() do when the user isn't allowed?
Returns falseThrows an exception that becomes a 403 responseLogs the user outRedirects to login always
Show answer
Answer: Throws an exception that becomes a 403 response