Laravel Gates

Laravel Gates

Written by Tony Lea on Oct 16th, 2020 Views Report Post

Laravel Gates allows you to authorize users from accessing certain areas of your application. You can easily define gates in your application and then use them to allow or deny access.

Let's start things off with a simple example of granting access without Gates, and then you'll see how we can use Gates as an alternative.

Simple Example

In our user's table, we'll assume we have a column named admin that can be a 1 or 0 based on whether the user is an admin or not. We could easily protect a section of our application by doing a simple check like this:

Route::get('administration', function(){
    if(auth()->check() && auth()->user()->admin){
        echo 'Welcome to the admin section';
    } else {
        echo 'You shall not pass';
    }
});

If the admin row is set 1 for a particular user, they will see the following output.

Admin access screenshot

Otherwise, they'll see the following:

Admin denied access

This is great! We have a simple way to allow or deny access to a specific section in our app. But here's the problem. What if we have these checks all over our application and want to modify user access. We would have to search our codebase and modify that check everywhere. Not very efficient.

Instead, we can define a Gate and use that throughout our application.

Defining Gates

To define a gate, we can open up our App\Providers\AuthServiceProvider.php file and add the following inside our boot() method:

public function boot()
{
    $this->registerPolicies();

    Gate::define('access-admin', function ($user) {
        return $user->admin;
    });
}

We can use this Gate anywhere we want to verify admin users throughout our application. In the next section, you’ll see how we can use this new Gate.

Using Gates

To use a Gate we can call the Gate::allows() or the Gate::denies() method like so:

Route::get('administration', function(){
    if (Gate::allows('access-admin')) {
        echo 'Welcome to the admin section';
    } else {
        echo 'You shall not pass';
    }
});

Notice the Gate::denies() method performs an inverse check of Gate::allows()

The great thing about these gates is that we can now change our definition at any time, and the authorization logic will be changed throughout our whole application.

Another reason to use Gates is to check permissions associated with data. Using a blog as an example, we can allow edit permissions to users on posts they have created.

We can pass data to our Gate to check whether or not a user has the authorization to perform an action.

Gates with data

Assume that our application has a Post table with a column of user_id, which contains the ID of the user who created it. We can define a Gate to determine if the user can edit a particular post like so:

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

Two arguments are being passed to our Gate definition. The first one is the $user object, which contains the authenticated user, and the second argument is our $post object.

Quick Note: The gate will return false if there is not an authenticated user.

This Gate will allow access if the authenticated user is the original author; otherwise, it will deny access.

Here's a quick example of how we might use our new edit-post gate.

Route::get('edit/{id}', function($id){
    
    $post = \App\Model\Post::find($id);
    
    if( Gate::allows('edit-post', $post) ){
        echo 'You can edit this post';
    } else {
        echo 'You shall not pass';
    }

});

Above, we are using Route Closures in our example, but we probably want to map this route to a controller. This will also let us use a new Authorize helper.

Authorize Helper

In addition to efficiency, another reason for using Gates are the helper functions.

Assuming that we map our route to a controller:

Route::get('edit/{id}', 'PostController@edit');

We can use the authorize() helper to check if the authenticated user has the authorization to edit a post:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function edit($id){

        $post = Post::find($id);
        $this->authorize('edit-post', $post);
        
    }
}

If a controller extends from the App\Http\Controllers\Controller base class, you can utilize the authorize() helper in the same way you would use the Gate::allow() function.

Last up, what if we wanted to check authorization inside of a view? We can use the @can blade helper to do just that.

Authorization in your Views

Given the following blade view:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $post->title }}</title>
</head>
<body>
    <h1>{{ $post->title }}</h1>
    <p>{!! $post->body !!}</p>
</body>
</html>

We can check that the current user is allowed to edit this post by using the awesome @can blade helper:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $post->title }}</title>
</head>
<body>
    <h1>{{ $post->title }}</h1>
    <p>{!! $post->body !!}</p>
    
    @can('edit-post', $post)
        <a href="edit/{{ $post->id }}">Edit Post</a>
    @endcan
    
</body>
</html>

If the authenticated user is the original author of this post, they will see an Edit Post button.

Using the @can helper can make our code easier to read and manage. You can also use the @cannot as the inverse.

Conclusion

That is the basics of using Gates in your Laravel application. Gates allow us to easily authorize access to areas of our application for specific users. This may also be referred to as an Access-Control List (ACL), a list of permissions associated with an object.

But we shouldn't overcomplicate things... In its simplest form, a Gate is used to allow or deny access. A user can either enter a Gate or not enter a Gate.

Because this tutorial is about letting users pass and not pass... It's necessary to send you off with this Gandalf image from Lord of the Rings.

shall-not-pass.jpg

To learn more about Laravel Gates, be sure to visit the documentation on Authorization..

Happy Authorizing 🔐

Comments (0)