Authorization in Laravel Gate and Police

In this chapter we are going to see an introduction to the Gates (Door) in Laravel which allow managing user authorization, that is, to indicate which parts of the system users can enter based on imposed rules.

Authentication and authorization

Before going into detail, we have to take into account two concepts that can cause confusion, authentication and authorization.

  • Authentication refers to when the user gives his credentials (username/password) at the system level, that is, the login is performed.
  • Authorization refers to what the user can do, that is, set limits; We previously saw how to do this process with the administrator and regular roles, but this time we are using Laravel's own service known as Gate.

You can imagine a Gate as a door (hence its name) in which, if a user has access to certain parts of the system, it means that the door is open, if he does not have access, then the door remains closed and he will not be able to access those parts of the system.

Clarified this, we are going to know how to use the Gate in Laravel; a Gate looks like this:

use Illuminate\Support\Facades\Gate;
***
Gate::define('update-post', function ($user, $post) {
    return $user->id == $post->user_id;
});

Here we can see 3 important elements:

  1. Yhe use of a Facade: Illuminate\Support\Facades\Gate.
  2. Define a key to indicate in a few words what is going to perform the operation, in the previous example it would be "update-post" which would be to update a post, therefore, we can use it in the controllers to edit a form ( The edit() and update()) functions.
  3. The following element corresponds to the rule or rules that you want to impose, in the previous example we indicated that the post must belong to a user, but you can place more, such as asking for the role of the user.

Another important point is the arguments, in order to use the authorization, the user must be authenticated and that is why the user argument is always present and is supplied internally by Laravel; the rest of the arguments are completely customizable and which ones you define depends on the rules you are going to establish.

Initial changes

In order to do some examples with the Gates, we're going to need to make some changes to the project, for example, add a user id column to the post table; to do this, we create a migration:

$ php artisan make:migration add_user_id_to_posts_table

We define the new column:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->foreignId('user_id')->constrained()
                ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropColumn('user_id');
        });
    }
};

And we execute the migration command:

$ php artisan migrate:fresh

This command will delete all the tables and generate them again, we do this since there are posts in the database and we cannot add a new column of foreign type (not null) to existing posts.

We apply the changes in the model:

app\Models\Post.php

class Post extends Model
{
    use HasFactory;

    protected $fillable = [***, 'user_id'];
    ***
}

And in the factory, we add the user id field:

database\factories\PostFactory.php

class PostFactory extends Factory
{
    public function definition(): array
    {
        // Post::truncate();
        $name = fake()->sentence;
        return [
            'title' => $name,
            'slug' => str($name)->slug(),
            'content' => $this->faker->paragraphs(20, true),
            'description' => $this->faker->paragraphs(4, true),
            'category_id' => $this->faker->randomElement([1, 2, 3]),
            'user_id' => $this->faker->randomElement([1, 2]),
            'posted' => $this->faker->randomElement(['yes', 'not']),
            'image' => $this->faker->imageUrl()
        ];
    }
}

With this, we have ready the initial changes to be able to create our first rule through the Gates.

Gate define y allow, key methods

There are two very important methods in Gates, the first is define() to define the Gate with the rules as we saw before:

app\Providers\AuthServiceProvider.php

public function boot(): void
{
    ***
    Gate::define('update-post', function ($user, $post) {
        return $user->id == $post->user_id;
    });
}

As you can see in the code above, the Gates are defined in AuthServiceProvider.php.

And to be able to use the previous Gate, we use the allows() function, since the previous Gate is to prevent users from modifying other users' posts, we use it in the editing methods:

app\Http\Controllers\Dashboard\PostController.php

use Illuminate\Support\Facades\Gate;
***
class PostController extends Controller
{
    public function edit(Post $post): View
    {
        if (!Gate::allows('update-post', $post)) {
            return abort(403);
        }
        ***
    }

    public function update(PutRequest $request, Post $post): RedirectResponse
    {
        if (!Gate::allows('update-post', $post)) {
            return abort(403);
        }
        ***
    }
}

And you will see that, from the Dashboard when you try to modify a post that does not belong to a user, a 403 error appears that of course you can customize with a redirect or any other operation.

- Andrés Cruz

En español

This material is part of my complete course and book; You can purchase them from the books and/or courses section, Curso y libro Laravel 11 con Tailwind Vue 3, introducción a Jetstream Livewire e Inerta desde cero - 2024.

Andrés Cruz

Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz In Udemy

I agree to receive announcements of interest about this Blog.