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.

Create a free account to save this page, take notes and track your progress. Sign in

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=Post with methods like update(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.

A gate, a policy with a before hook, and a 403
<?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 numbers

Run this on your computer — see “Get Started” for setup.

Test yourself

What does Gate::authorize() do when the user isn't allowed?

  1. Returns false
  2. Throws an exception that becomes a 403 response
  3. Logs the user out
  4. Redirects to login always
Show answer

Answer: Throws an exception that becomes a 403 response

Was this page helpful?