Advertisement
  1. Code
  2. PHP
  3. Laravel

Gates and Policies in Laravel

Scroll to top
Read Time: 14 min

Today, we're going to discuss the authorization system of the Laravel web framework. The Laravel framework implements authorization in the form of gates and policies. After an introduction to gates and policies, I'll demonstrate the concepts by implementing a custom example.

I assume that you're already aware of the built-in Laravel authentication system as that's something essential in order to understand the concept of authorization. Obviously, the authorization system works in conjunction with the authentication system to identify the legitimate user session.

If you're not aware of the Laravel authentication system, I would highly recommend going through the official documentation, which provides you with hands-on insight into the subject.

Laravel's Approach to Authorization

By now, you should already know that the Laravel authorization system comes in two flavors—gates and policies. Although it may sound like a complicated affair, I would say it's pretty easy to implement once you get the hang of it!

Gates allow you to define an authorization rule using a simple closure-based approach. In other words, when you want to authorize an action that's not related to any specific model, the gate is the perfect place to implement that logic.

Let's have a quick look at what gate-based authorization looks like:

1
...
2
...
3
Gate::define('update-post', function ($user, $post) {
4
  return $user->id == $post->user_id;
5
});
6
...
7
...

The above snippet defines the authorization rule update-post that you could call from anywhere in your application.

On the other hand, you should use policies when you want to group the authorization logic of any model. For example, let's say you have a Post model in your application, and you want to authorize the CRUD actions of that model. In that case, it's the policy that you need to implement.

1
class PostPolicy
2
{
3
  public function view(User $user, Post $post) {}
4
  public function create(User $user) {}
5
  public function update(User $user, Post $post) {}
6
  public function delete(User $user, Post $post) {}
7
}

As you can see, it's a pretty simple policy class that defines the authorization for the CRUD actions of the Post model.

So that was an introduction to gates and policies in Laravel. From the next section onwards, we'll go through a practical demonstration of each element.

Gates

In this section, we'll see a real-world example to understand the concept of gates.

How to Create a Custom Gate

More often than not, you end up looking at the Laravel service provider when you need to register a component or a service. Following that convention, let's go ahead and define our custom gate in the app/Providers/AuthServiceProvider.php as shown in the following snippet.

1
<?php
2
namespace App\Providers;
3
 
4
use Illuminate\Support\Facades\Gate;
5
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
6
use Illuminate\Http\Request;
7
 
8
class AuthServiceProvider extends ServiceProvider
9
{
10
  /**

11
   * The policy mappings for the application.

12
   *

13
   * @var array

14
   */
15
  protected $policies = [
16
    'App\Model' => 'App\Policies\ModelPolicy',
17
  ];
18
 
19
  /**

20
   * Register any authentication / authorization services.

21
   *

22
   * @return void

23
   */
24
  public function boot()
25
  {
26
    $this->registerPolicies();
27
     
28
    Gate::define('update-post', function ($user, $post) {
29
      return $user->id == $post->user_id;
30
    });
31
  }
32
}

In the boot method, we've defined our custom gate:

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

While defining a gate, it takes a closure that returns either TRUE or FALSE based on the authorization logic that's defined in the gate definition. Apart from the closure function, there are other ways you could define gates.

For example, the following gate definition calls the controller action instead of the closure function.

1
Gate::define('update-post', 'ControllerName@MethodName');

How to Use Our Custom Gate

Now, let's go ahead and add a custom route so that we can go through a demonstration of how gate-based authorization works. In the routes file routes/web.php, let's add the following route.

1
Route::get('service/post/gate', 'PostController@gate');

Let's create an associated controller file app/Http/Controllers/PostController.php as well.

1
<?php
2
namespace App\Http\Controllers;
3
 
4
use App\Http\Controllers\Controller;
5
use App\Post;
6
use Illuminate\Support\Facades\Gate;
7
 
8
class PostController extends Controller
9
{
10
  /* Make sure you don't use Gate and Policy altogether for the same Model/Resource */
11
  public function gate()
12
  {
13
    $post = Post::find(1);
14
 
15
    if (Gate::allows('update-post', $post)) {
16
      echo 'Allowed';
17
    } else {
18
      echo 'Not Allowed';
19
    }
20
     
21
    exit;
22
  }
23
}

In most cases, you'll end up using either the allows or denies method of the Gate facade to authorize a certain action. In our example above, we've used the allows method to check if the current user is able to perform the update-post action.

Users with sharp eyes would have noticed that we've only passed the second argument $post to the closure. The first argument, the current logged-in user, is automatically injected by the Gate facade.

So that's how you're supposed to use gates to authorize actions in your Laravel application. The next section is all about how to use policies, should you wish to implement authorization for your models.

Policies

As we discussed earlier, when you want to logically group your authorization actions for any particular model or resource, it's the policy you're looking for.

How to Create a Custom Policy

In this section, we'll create a policy for the Post model that will be used to authorize all the CRUD actions. I assume that you've already implemented the Post model in your application; otherwise, something similar will do.

The Laravel artisan command is your best friend when it comes to creating stubbed code. You can use the following artisan command to create a policy for the Post model.

1
$php artisan make:policy PostPolicy --model=Post

As you can see, we've supplied the --model=Post argument so that it creates all the CRUD methods. In the absence of that, it'll create a blank policy class. You can locate the newly created policy class at app/Policies/PostPolicy.php.

1
<?php
2
3
namespace App\Policies;
4
5
use App\Post;
6
use App\User;
7
use Illuminate\Auth\Access\HandlesAuthorization;
8
9
class PostPolicy
10
{
11
    use HandlesAuthorization;
12
13
    /**

14
     * Determine whether the user can view any models.

15
     *

16
     * @param  \App\User  $user

17
     * @return mixed

18
     */
19
    public function viewAny(User $user)
20
    {
21
        //

22
    }
23
24
    /**

25
     * Determine whether the user can view the model.

26
     *

27
     * @param  \App\User  $user

28
     * @param  \App\Post  $post

29
     * @return mixed

30
     */
31
    public function view(User $user, Post $post)
32
    {
33
        //

34
    }
35
36
    /**

37
     * Determine whether the user can create models.

38
     *

39
     * @param  \App\User  $user

40
     * @return mixed

41
     */
42
    public function create(User $user)
43
    {
44
        //

45
    }
46
47
    /**

48
     * Determine whether the user can update the model.

49
     *

50
     * @param  \App\User  $user

51
     * @param  \App\Post  $post

52
     * @return mixed

53
     */
54
    public function update(User $user, Post $post)
55
    {
56
        //

57
    }
58
59
    /**

60
     * Determine whether the user can delete the model.

61
     *

62
     * @param  \App\User  $user

63
     * @param  \App\Post  $post

64
     * @return mixed

65
     */
66
    public function delete(User $user, Post $post)
67
    {
68
        //

69
    }
70
71
    /**

72
     * Determine whether the user can restore the model.

73
     *

74
     * @param  \App\User  $user

75
     * @param  \App\Post  $post

76
     * @return mixed

77
     */
78
    public function restore(User $user, Post $post)
79
    {
80
        //

81
    }
82
83
    /**

84
     * Determine whether the user can permanently delete the model.

85
     *

86
     * @param  \App\User  $user

87
     * @param  \App\Post  $post

88
     * @return mixed

89
     */
90
    public function forceDelete(User $user, Post $post)
91
    {
92
        //

93
    }
94
}

Let's replace it with the following code.

1
<?php
2
namespace App\Policies;
3
 
4
use App\User;
5
use App\Post;
6
use Illuminate\Auth\Access\HandlesAuthorization;
7
 
8
class PostPolicy
9
{
10
  use HandlesAuthorization;
11
 
12
  /**

13
   * Determine whether the user can view the post.

14
   *

15
   * @param  \App\User  $user

16
   * @param  \App\Post  $post

17
   * @return mixed

18
   */
19
  public function view(User $user, Post $post)
20
  {
21
    return TRUE;
22
  }
23
 
24
  /**

25
   * Determine whether the user can create posts.

26
   *

27
   * @param  \App\User  $user

28
   * @return mixed

29
   */
30
  public function create(User $user)
31
  {
32
    return $user->id > 0;
33
  }
34
 
35
  /**

36
   * Determine whether the user can update the post.

37
   *

38
   * @param  \App\User  $user

39
   * @param  \App\Post  $post

40
   * @return mixed

41
   */
42
  public function update(User $user, Post $post)
43
  {
44
    return $user->id == $post->user_id;
45
  }
46
 
47
  /**

48
   * Determine whether the user can delete the post.

49
   *

50
   * @param  \App\User  $user

51
   * @param  \App\Post  $post

52
   * @return mixed

53
   */
54
  public function delete(User $user, Post $post)
55
  {
56
    return $user->id == $post->user_id;
57
  }
58
}

To be able to use our policy class, we need to register it using the Laravel service provider as shown in the following snippet.

1
<?php
2
namespace App\Providers;
3
 
4
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
5
use Illuminate\Http\Request;
6
use App\Post;
7
use App\Policies\PostPolicy;
8
 
9
class AuthServiceProvider extends ServiceProvider
10
{
11
  /**

12
   * The policy mappings for the application.

13
   *

14
   * @var array

15
   */
16
  protected $policies = [
17
    Post::class => PostPolicy::class
18
  ];
19
 
20
  /**

21
   * Register any authentication / authorization services.

22
   *

23
   * @return void

24
   */
25
  public function boot()
26
  {
27
    $this->registerPolicies();
28
  }
29
}

We've added the mapping of our policy in the $policies property. It tells Laravel to call the corresponding policy method to authorize the CRUD action.

You also need to register the policies using the registerPolicies method, as we've done in the boot method.

Policy Methods Without Model

The create method in the PostPolicy policy class takes only a single argument, unlike other model methods that take two arguments. Generally, the second argument is the model object, which you're going to use when you perform authorization. But, in the case of the create method, you just need to check if the user in question is allowed to create a new post.

In the next section, we'll see how to use our custom policy class.

How to Use Our Custom Policy Class

Moving further, let's create a couple of custom routes in the routes/web.php file so that we can test our policy methods there.

1
Route::get('service/post/view', 'PostController@view');
2
Route::get('service/post/create', 'PostController@create');
3
Route::get('service/post/update', 'PostController@update');
4
Route::get('service/post/delete', 'PostController@delete');

Finally, let's create an associated controller at app/Http/Controllers/PostController.php.

1
<?php
2
namespace App\Http\Controllers;
3
 
4
use App\Http\Controllers\Controller;
5
use App\Post;
6
use Illuminate\Support\Facades\Auth;
7
 
8
class PostController extends Controller
9
{
10
  public function view()
11
  {
12
    // get current logged in user

13
    $user = Auth::user();
14
     
15
    // load post

16
    $post = Post::find(1);
17
     
18
    if ($user->can('view', $post)) {
19
      echo "Current logged in user is allowed to update the Post: {$post->id}";
20
    } else {
21
      echo 'Not Authorized.';
22
    }
23
  }
24
 
25
  public function create()
26
  {
27
    // get current logged in user

28
    $user = Auth::user();
29
 
30
    if ($user->can('create', Post::class)) {
31
      echo 'Current logged in user is allowed to create new posts.';
32
    } else {
33
      echo 'Not Authorized';
34
    }
35
 
36
    exit;
37
  }
38
 
39
  public function update()
40
  {
41
    // get current logged in user

42
    $user = Auth::user();
43
 
44
    // load post

45
    $post = Post::find(1);
46
 
47
    if ($user->can('update', $post)) {
48
      echo "Current logged in user is allowed to update the Post: {$post->id}";
49
    } else {
50
      echo 'Not Authorized.';
51
    }
52
  }
53
 
54
  public function delete()
55
  {
56
    // get current logged in user

57
    $user = Auth::user();
58
     
59
    // load post

60
    $post = Post::find(1);
61
     
62
    if ($user->can('delete', $post)) {
63
      echo "Current logged in user is allowed to delete the Post: {$post->id}";
64
    } else {
65
      echo 'Not Authorized.';
66
    }
67
  }
68
}

There are different ways you could authorize your actions using policies. In our example above, we’ve used the User model to authorize our Post model actions.

The User model provides two useful methods for authorization purposes—can and cant. The can method is used to check if the current user is able to execute a certain action. And the counterpart of the can method, the cant method, is used to determine the inability of the action execution.

Let’s grab the snippet of the view method from the controller to see exactly what it does.

1
public function view()
2
{
3
  // get current logged in user

4
  $user = Auth::user();
5
   
6
  // load post

7
  $post = Post::find(1);
8
   
9
  if ($user->can('view', $post)) {
10
    echo "Current logged in user is allowed to update the Post: {$post->id}";
11
  } else {
12
    echo 'Not Authorized.';
13
  }
14
}

Firstly, we load the currently logged-in user, which gives us the object of the User model. Next, we load an example post using the Post model.

Moving ahead, we’ve used the can method of the User model to authorize the view action of the Post model. The first argument of the can method is the action name that you want to authorize, and the second argument is the model object that you want to get authorized against.

That was a demonstration of how to use the User model to authorize the actions using policies. Alternatively, you could use the controller helper as well, if you’re in the controller while authorizing a certain action.

1

2
$this->authorize('view', $post);
3

As you can see, you don’t need to load the User model if you use the controller helper.

How to Use Policies With Middleware

Alternatively, Laravel also allows you to authorize actions by using middleware. Let's quickly look at the following snippet to understand how to use middleware to authorize model actions.

1
Route::put('/post/{post}', function (Post $post) {
2
    // Currently logged-in user is allowed to update this post.... 

3
})->middleware('can:update,post');

In the above example, it'll use the can middleware. We're passing two arguments: the first argument is the action which we want to authorize, and the second argument is the route parameter. Based on the rules of implicit model binding, the second parameter is automatically converted to the Post model object, and it's passed as the second argument. If the currently logged-in user is not authorized to perform the update action, Laravel returns a 403 status code error.

How to Use Policies With Blade Templates

You can also use policies in blade templates as well, if you want to display a code snippet if the logged-in user is authorized to perform a specific action. Let's quickly look at how it works.

1
@can('delete', $post)
2
    <!-- Display delete link here... -->
3
@endcan

In the above case, it'll only display the delete link to a user who is authorized to perform the delete action on the Post model.

So that was the concept of policies at your disposal, and it’s really handy while authorizing a model or a resource as it allows you to group the authorization logic in one place.

Just make sure that you don’t use gates and policies altogether for the same actions of the Model, otherwise it’ll create issues. That’s it from my side for today, and I’ll call it a day!

Conclusion

Today, it was Laravel authorization that took the center stage in my article. At the beginning of the article, I introduced the main elements of Laravel authorization, gates and policies.

Following that, we went through creating our custom gate and policy to see how it works in the real world. I hope you’ve enjoyed the article and learned something useful in the context of Laravel.

For those of you who are either just getting started with Laravel or looking to expand your knowledge, site, or application with extensions, we have a variety of things you can study on Envato Market.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.