Authorization in Laravel Gate and Police
Authorization in Laravel is one of those features that separates a basic project from a truly professional one. In this article, I am going to explain to you, with real examples and functional code, how to use Gates and Policies to control user access in your application, using, of course, the system of Localizations and Translations in Laravel to correctly convey the message.
We are going to look at an introduction to Gates in Laravel, which allow you to manage user authorization, that is, to indicate which parts of the system users can access based on imposed rules.
Introduction to Authorization in Laravel
Difference between authentication and authorization
Before touching code, it must be clear that authentication and authorization are not the same thing.
- Authentication: Authentication refers to when the user provides their credentials (username/password) at the system level, that is, the login is performed.
- Authorization: Authorization refers to what the user can do, that is, setting limits; previously we saw how to do this process with administrator and regular roles, but, in this opportunity, we are using a Laravel-specific service known as Gate.
In my case, I understood it better by imagining a Gate as a door. If the user has permission, the door opens; if not, it remains closed. Laravel precisely implements that concept: a Gate decides who can pass and who cannot.
Why use Gates and Policies instead of traditional roles?
Using roles like "admin" or "regular user" can work at the beginning, but it doesn't scale well. Gates and Policies allow you to define more precise and centralized rules, facilitating code maintenance and security.
What are Gates in Laravel
Definition and basic operation
Gates are functions that determine whether a user can execute a specific action. They are normally defined in the file:
app/Providers/AuthServiceProvider.phpHow to create a Gate in AuthServiceProvider
For example, this Gate allows updating only the posts created by the same user:
use Illuminate\Support\Facades\Gate;
Gate::define('update-post', function ($user, $post) {
return $user->id == $post->user_id;
});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 the door is open; if they do not have access, then the door remains closed and they will not be able to access those parts of the system.
Here we can see 3 important elements:
- The use of a Facade: Illuminate\Support\Facades\Gate.
- Defining a key to indicate in a nutshell what the operation is going to perform. In the previous example, it would be "update-post", which would be for updating a post. Therefore, we can use it in the controllers to edit a form (The edit() and update() functions).
- The next element corresponds to the rule or rules you want to impose. In the previous example, we indicated that the post must belong to a user, but you can add more, such as asking for the user's role.
Another important point is the arguments. To be able to use authorization, the user must be authenticated, which is why the user argument is always present and is supplied internally by Laravel; the rest of the arguments are completely customizable, and the ones you define depend on the rules you are going to establish.
Practical Example: Restrict Post Editing
Content Index
- Introduction to Authorization in Laravel
- Why use Gates and Policies instead of traditional roles?
- What are Gates in Laravel
- Definition and basic operation
- Practical Example: Restrict Post Editing
- Gate define and allow, key methods
- What are Policies in Laravel
- Using Gates and Policies in controllers and views
- ⚠️ Common Errors and How to Avoid Them
- 403 Error when applying a Gate
- Policies not registered or incorrectly linked
- Best Practices and Experience Tips
- Conclusion
- ❓ Frequently Asked Questions
To be able to do some examples with Gates, we will need to make some changes to the project, for example, adding a user ID column to the posts table; to do this, we create a migration:
$ php artisan make:migration add_user_id_to_posts_tableTest data with PostFactory:
'user_id' => $this->faker->randomElement([1, 2]),This allowed the posts to be correctly linked to users and to test the Gate without errors.
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:freshThis command deletes all tables and regenerates them. We do this because there are existing posts in the database and we cannot add a new foreign key (not null) column to existing posts.
We apply the changes to 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 the initial changes ready to be able to create our first rule using the Gate.
Gate define and allow, key methods
There are two very important methods in Gates. The first one is define() to define the Gate with the rules just 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 previous code, 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, which you can of course customize with a redirection or any other operation.
What are Policies in Laravel
When to use a Policy
Policies are classes dedicated to handling the authorization for an entire model.
If the Gate is a "specific door," a Policy is "the guard of the whole building."
It is convenient to use Policies when:
- You have many rules for the same model.
- You want to keep the code tidy.
- You want to take advantage of automatic methods like viewAny, update, delete, etc.
- Creating a Policy with Artisan step by step
Laravel makes it simple:
$ php artisan make:policy PostPolicy --model=PostThis creates a file in app/Policies/PostPolicy.php with base methods.
Registering and applying a Policy to a model
Edit your AuthServiceProvider and register the class:
protected $policies = [
Post::class => PostPolicy::class,
];Now Laravel knows which Policy to apply for that model.
Complete example of authorization with Policy
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}And in the controller:
$this->authorize('update', $post);Since I started using Policies, my code has become cleaner and much easier to maintain.
Using Gates and Policies in controllers and views
Using Gate::allows() and can() in controllers
In controllers, you can authorize like this:
if (Gate::denies('update-post', $post)) {
abort(403);
}Or use the more readable helper:
if ($user->can('update', $post)) { // allowed action }Direct Authorization in Blade with @can and @cannot
In Blade views, Laravel allows you to simplify it with directives:
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Editar</a>
@endcanIntegration with middleware to protect routes
You can also use middleware on the routes:
Route::put('/posts/{post}', [PostController::class, 'update'])
->middleware('can:update,post');This prevents unauthorized users from even reaching the controller.
⚠️ Common Errors and How to Avoid Them
403 Error when applying a Gate
Often it's not a Gate failure, but that the user does not meet the conditions. Make sure $user and $post are correctly passed.
Migration and foreign key issues
When I added user_id to my table, I had to run:
$ php artisan migrate:freshTo regenerate the database without key conflicts.
Policies not registered or incorrectly linked
If your Policy does not execute, check that it is registered in AuthServiceProvider and that you use the exact same model.
Best Practices and Experience Tips
- Keep rules centralized in Policies
- Avoid dispersing the logic through controllers. Centralize everything in your Policies.
- Name Gates and Policies coherently
- Use short, descriptive names: update-post, delete-comment, publish-article.
- Use testing to verify authorization
- Laravel allows testing Gates easily:
- $this->assertTrue(Gate::forUser($user)->allows('update-post', $post));
- Combine roles and Policies for complex projects
- In large systems, I usually combine roles with Policies to gain more granular access control.
Conclusion
Laravel offers a robust, secure, and elegant authorization system.
Gates serve you for specific rules; Policies, for complete models.
When you combine both, you achieve a clean, centralized, and easy-to-maintain flow.
In my experience, mastering these tools gives you a huge advantage in any real Laravel project.
❓ Frequently Asked Questions
What is the difference between a Gate and a Policy in Laravel?
A Gate handles a specific action; a Policy, the actions of an entire model.
Where are Gates defined in Laravel?
Usually in app/Providers/AuthServiceProvider.php.
How to handle a 403 error?
Verify that the user is authenticated and meets the conditions of the Gate or Policy.
Next step, the domain/subdomain management system in Laravel
I agree to receive announcements of interest about this Blog.
Gates are an authorization feature that allow you to define policies to control access to certain parts of your application.