Laravel Gate & Policy, Part — 1: Understanding Laravel Gate

Syed Sirajul Islam Anik
5 min readMay 27, 2020

--

By: Masaaki Komori on https://unsplash.com/photos/_we0BQQewBo

There is a part two of this article. You can find it HERE.

The documentation of Laravel Authorization is pretty straightforward & easy. If you haven’t seen it yet, it’s okay. I will try to rephrase the same thing with my own words. I hope you’ll get to know this topic thoroughly. Let’s jump into it.

Few notes

  • Authorization & Authentication are different. The facade Auth is for authentication. Which validates the credentials with the stored data. If the user fails to validate the credential, the HTTP status code will be: 401
  • Authorization, on the other hand, means if you’re allowed to perform a task or not. In movies, we see that someone intrudes himself into a system he was not authorized bypassing some security flaws. That is called authorization. If you are authorized means you’re allowed. The HTTP status code for Unauthorized is 403.
  • Authorization always takes part after the Authentication. If a system can’t identify who you’re, then how’ll it decide if you’re allowed or not?

How to authorize?

In Laravel, there are two ways to authorize a user. One approach is closure based, another approach is class-based. Closure based approach is called Gates. The class-based approach is called policy. It this article I’ll only mention Gates. The gates approach is more like the closure based route definition.

Closure based approach aka Gates

Closure based approach uses the Gate Facade to define the authorizations. The facade proxies the calls toIlluminate/Auth/Access/Gate.php. A sample code is like below. You can add the following snippet anywhere you wish. But it’s a good approach to keep the same type of code altogether. So, it’s good to use app/Providers/AuthServiceProvider.php‘s boot method to put in which is already shipped with Laravel. In closure based approach, you use define method to declare new authorization criteria which takes two parameters. The first one is the name, which will be used later as a reference to authorize the user. And secondly the closure itself. Remember one thing that the closure gets the currently logged in user by default in its first param.

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

In the above example, we’re allowing only the user id 1 to have the authority to create a post. Other than that, nobody will have the authorization to add a new post to your website.

To check if the logged-in user is allowed to create a new post, you need to call like following.

Gate::allows('create-post');

That will return a boolean value denoting if the user is allowed or rejected. Simple, right? Do you see that $user variable in the closure’s first param? It’s passed to all the defined gates and policies by default. It’s the currently logged-in user.

This way, you can define as many gates as you want. A few samples below.

Gate::define('edit-post', function ($user, $post) {
return $user->id == $post->user_id;
});

Gate::define('delete-post', function ($user, $post) {
return $user->id == $post->user_id;
});
// Gate::allows('delete-post', Post::find(10));
// Gate::allows('edit-post', $post)

Whenever you need the second or extra parameter in your closure, you’ll need to pass these from wherever you’re calling. You can pass the extra parameters in two ways. If you need only one parameter next to $user variable, then you can pass it as in the Gate::allows example. But if you require more than one parameter in your closure, then you’ll need to use an array as the second params like the following.

Gate::allows('gate-name', [$param1, $param2]);

In this way, these values in your array will be passed as the parameters in sequence after $user variable to your closure.

Is allows is the only method to authorize?

No, there are several methods.

  • allows — Checks if the given ability passes.
  • denies — Negates the allows conditions.
  • check — Checks if a single or array of abilities are allowed.
  • any — Checks if any of single or array of abilities passes.
  • none — Negates the any conditions.
  • authorize — Checks if the ability is allowed or throws an Illuminate\Auth\Access\AuthorizationException exception.
// use Illuminate\Auth\Access\ResponseGate::allows(string $ability, $arguments = []): bool
Gate::denies(string $ability, $arguments = []): bool
Gate::check(array|string $abilities, $arguments = []): bool
Gate::any(array|string $abilities, $arguments = []): bool
Gate::none(array|string $abilities, $arguments = []): bool
Gate::authorize(string $ability, $arguments = []): Response

Anything more?

Suppose, you want to check an authorization against a specific user not just the currently logged-in user, then you can use forUser($userId).

Gate::forUser(User::find(10))->allows('edit-post', Post::find(20));

The above code will then check if the user id 10 is allowed to edit the post with the id of 20 or not.

Bear in mind

If you somewhere come face to a situation when you’ll need to approve all the authorization for a user or a user will have a privilege to specific ability, then you can use the before method of Gates. You can add as many before callbacks as you want. But if any of your before callback returns a non-null value, it will be returned as your authorization logic and your referenced authorization gate names will not be used. Like, before you can add multiple after callbacks too. But, if your authorization before or custom doesn’t return any value, then the after will overwrite that value.

Gate::before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
Gate::after(function ($user, $ability, $result, $arguments) {
if ($user->isSuperAdmin()) {
return true;
}
});

That’s all about the gates. You can dig in by going to the concrete Gate definition class.

Previously we defined the gates as

Gate::define('ability', function (User $user){});

But we can use classes as we do for the controllers in web.php or api.php. To do so we can define gates as follows.

<?phpnamespace App;class CustomPolicy 
{
public function editPost (User $user, Post $post) {
return $user->id === $post->user_id;
}
}

Now, in your app/Providers/AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
public function boot () {
// to keep the code in one-line, used App namespace.
// closure is replaced with ClassName@methodName format.
Gate::define('edit-post', 'App\CustomPolicy@editPost');
}
}

This way your provider stays clean. And, you can achieve the same for what we did in our previous examples.

You can find the second part of this article HERE.

Happy coding ❤

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote