Dead simple permissions for Laravel

Permissions can be complex. Here’s an easy solution.

Warrick Bayman
7 min readMar 4, 2020
Photo by Rob King on Unsplash

tl;dr: If you’re not interested in setting this up yourself and would rather just install a package, I wrote one called Deadbolt. Check it out on GitHub.

I’ve built a number of Laravel applications over the years and most of them had some sort of authorisation requirement. Some users should have access to certain resources, while other users should not. It’s easy, right? Add a boolean column to your users table that gives users access to that resource if it’s set to true. Done!

Sure, that works. But it’s really not very flexible. What happens when you have lots of resources? What if you need to protect other parts of your application? That simple boolean “flag” is quickly going to show its shortcomings. This is where you quickly start to realise that you need some form of permissions system. Luckily there are quite a few really awesome solutions to choose from, but by far the most popular (for good reason) is the laravel-permission package from the guys at Spatie. It has everything you could possibly want out of a permissions package for Laravel. If you’re looking for a really flexible, feature-rich permissions package, then that is definitely the right way to go. But every now and again, you just don’t need all of that. Sometimes, all you really want sits somewhere between the boolean flags and the power of Spatie’s solution.

I think that the approach I describe here fills that gap. It’s simple and is especially useful when you’re dealing with an SPA frontend (or my favourite at the moment: IntertiaJS). The idea is that you store assigned permissions directly in your users table as a JSON encoded string which is then available every single time you reference the User model. No need for additional database tables.

I’ve also never really felt comfortable storing application permissions in a database table. Roles, sure, but permissions are often tied to the actions they authorize. Those actions generally are not described in a database table, so why do we do so with the permissions that authorise them?

Okay, let’s get on with it, then.

Update the user table migration

First, we need a place to store the permissions we’re going to give our users. I’ve already said that we’ll store assigned permissions right in the users database table. So update your users database table migration to include a permissions column.

create_users_table.php migration

We’ll need to update our User model to cast that column as a JSON object. Add the following protected property to User.php:

User.php

Now when we use $user->permissions, we’ll get a PHP array of the permissions assigned to that user. That’s already better than the series of boolean flags I mentioned earlier.

Just remember that string columns usually have a fixed length. If you have lots of permissions, you might need something a bit longer.

Define the available permissions

You can define a new config file for this, but it’s perfectly fine to add things to the config files that are already included with Laravel. That’s kinda what they’re there for, anyway.

Add a new array to the end of the auth.php file in your config directory. We’ll use this to list the actual permissions we can potentially give to users.

auth.php

The useful part of this is that we can now get a list of available permissions using the config helper method:

$permissions = config('auth.permissions');

We can already start assigning permissions to our users when updating or creating them:

Creating a new user with permissions

Using Laravel policies

I strongly recommend that you use policy classes to authorise actions on resources. As an example, let’s say we have an Article model that represents blog articles in our database. Our users should have separate permissions to create, edit or delete articles. We can create a new policy for articles using a simple Artisan console command:

php ./artisan make:policy ArticlePolicy

This will create a new /app/Policies/ArticlePolicy.php file. We can create methods in there that represent the actions we want to authorise. Write create, edit, and delete methods that return a boolean. We’ll simply check if the required permission is in the array of permissions we can get from the User model passed into the method.

ArticlePolicy.php

Now that our policy is complete, Laravel provides a simple authorize method that you can use from within your controllers that will let you authorize users using the policy class we created. So for example, in our create method on our ArticleController class, we can do this:

As a user, if you now try to make a request for the /articles/create URI and the articles.create permission doesn’t exist in your permission array, you’ll get a 401 Unauthorized error.

That’s pretty simple, and easy to set up. It’s not all that easy to use right now. Especially if you’re going to need to check more than one permission.

Create a service class

Let’s create a simple service class that we can reach for when we need to manage a users permissions. That way we don’t need to mess around with the user model directly and we can add some useful additional features along the way. Create a new file called Services/PermissionService.php. We’ll write a few simple methods to help manage the permissions for our users. Let’s start with something simple:

PermissionService.php

Right now, all this class can do is return an array of the permissions we put into the auth.php config file. That’s not exactly exciting, or particularly useful. So let’s add a method that we can use to give our users new permissions.

We don’t want to add permissions that don’t actually exist, so we’ll first check that a permission is in the array we added to the auth config. Only permissions that are in that array will then be added to the user. Let’s write a give method that takes a User instance and an array of permissions which we can call when we’re ready to assign permissions.

PermissionService.php

This method returns the service class itself so we can chain methods together if we want. Also, it might be better to rather fail here if a permission doesn’t exist instead of simply ignoring it. That way, you’ll be able to catch potential typo’s you might have missed.

We can also add a super method which will automatically give the user ALL the available permissions in one go. The super method just calls the give method and passes in all the available permissions:

PermissionService.php

Okay, great! We can give users permissions. But what if we want to take permissions away? Let’s write a revoke method and a clear method to do just that:

PermissionSerivce.php

Create a facade

I’m not going to explain facades here. You can read up about how facades work in the Laravel documentation. Just know that a facade is a really easy way to create a new instance of a class and get access to the methods on that class through a nice, easy-to-read interface.

You could easily use this service class without the facade if you don’t want to write a facade, but it does makes it a little sexier. So create a new class in Facades/Permissions.php:

Permissions.php

The facade will simply tell Laravel how to find the service class we already created. We do this by using a “key” that we’ll bind into the Laravel container. The facades getFacadeAccessor method should return that key.

Once you’ve got your facade all sorted out, we need to register it. This is commonly done in the AppServiceProvider in your Providers directory. Update the register method with the following:

AppServiceProvider.php

That’s all we need to do to get the facade registered. Now we can use our new facade like this:

Permissions::give($user, ['articles.create']);
$user->save();

How about testing permissions though? At the moment, our ArticlePolicy class is checking if a user as a permission by checking if that permission exists within the array stored in the permissions column. There’s nothing really wrong with that, but we can do better. So let’s add a method to the PermissionService that we can use to test if a user has a specific permission. We’re just moving the in_array logic from the policy into our service class.

PermissionService.php

Now let’s update the ArticlePolicy class so that it uses the Permissions::has() method we just created:

ArticlePolicy.php

And done!

That’s it. If we need to add permissions to our system, we just update the permissions array in the auth.php config. We can add policies as we need and use the facade to give and revoke permissions. There’s a lot of room for improvement here, but the idea was to create something simple that is a little more flexible than adding an admin boolean column.

And JavaScript…

If you need to have access to the users permissions from within a JavaScript app, because Laravel will do the hard work for you by returning an array of your User model, you get the cast permissions array as well for free! And since PHP arrays can easily be encoded as JSON strings, there’s really not much you need to do to get the users permissions available in your JavaScript client.

I like using InteriaJS at the moment, so if I return the user to an Inertia page from a controller method like this:

return Inertia::render('Pages/Articles/Create', [
'user' => Auth::user(),
]);

… in my Vue template I can easily do something like this:

Component.vue

No need to make extra HTTP requests to the application just to check if a user has permission to complete a task. I have the user object, so I have their permissions as well. If I share the authenticated user with all my Inertia views then I have those permissions everywhere, which is really handy.

The Package

Waring! Blatant self-promotion ahead…

I’ve used this approach a few times now, and even in one fairly large project and it works like a charm. Anyway, to make my life easier, I created a package for Laravel called Deadbolt and it’s similar to what I’ve described here, but with perhaps a little more grace and elegance, if I say so myself. The package has a bunch more features than this article and is probably a lot less error prone, but the basic idea is the same.

The package is MIT licensed, so use how you want. It’s still fairly new, but I’m using it a bunch at the moment, so it currently gets fairly regular updates.

--

--

Warrick Bayman

Programmer, musician, cyclist (well... I own a bike), husband and father.