Simple JWToken ACL for Laravel (or anyone else)

Jeff Madsen
Jan 29, 2017 · 4 min read
Image for post
Image for post

Over at Mahana Designs we usually work with stateless session (meaning that there are no server sessions but instead the user information is repeatedly passed back to the server with each request in the token). Most of our work involves single users making requests from mobile apps, and so a full-scale ACL library isn’t normally necessary, but we include a simple option to allow our clients to easily add one should they later need to.

The technique we use is quite simple and relies on a combination of the Laravel Middleware and string comparison. There is absolutely no reason why you couldn’t use this same idea in any other framework. I will try my best to explain this process in framework neutral terms to help you understand how you might modify it to fit your own needs.

To start, we’ll pull in Tymon Design’s JWT-Auth package for Laravel. Remember that Json Web Token’s ( JWT) are a standard, not a specific library, and so you have a great many packages in all languages to choose from. Here’s a fantastic resource to learn more about them and even debug while working: Json Web Tokens.

The first part of our code is the login function:

Image for post
Image for post
The LoginController->login() function

We are no longer relying on setting server sessions during login; instead, we are building a token that we will return and will subsequently be passed along with all of our requests via the Authorization header.

JWT-Auth allows us to easily add additional payload to our token; we will use that to build “claim strings” that store an array of strings representing the ACL privileges we are going to allow this user to have. I’ll explain in greater detail below, but in this example we are giving them “open” and the ability to access their own profile. We check the user credentials normally and return the token.

This is how the token will be used in Postman:

Image for post
Image for post
The Authorization Header

If you are not familiar with loading tokens dynamically into Postman headers, have a look at my Quick Tip post that gives you a full run-down on the procedure.

Now that we have created the token and learned to send it back to our server, let’s see what our server is actually doing to handle this. We have our routes setup to work with the Laravel middleware such as this:

Image for post
Image for post
Routes to view authenticated only information and manage own profile

with a middleware library set up for this called, appropriately, ACLMiddleware:

Image for post
Image for post
ACLMiddleware handle() function

So finally we get to the actual logic of the system. Let us pretend we are accessing the ‘information/latest’ route. Our middleware sees that it should pass the value “cbj.open” to the “acl” middleware (which is registered in our Kernel, as per the documentation).

The middleware gets the claim strings from the token payload. You remember that that is just an array of string, [‘cbj.open’, ‘cbj.users.profile.self’]. It will do a check that either passes the $request on to the $next closure, or blocks it.

The check could be a simple in_array(), but that would really limit its usefulness to us. Instead, we have a system of claim strings set up in a hierarchical fashion that works like this:

cbj — super admin
cbj.open — a general claim to authorized routes
cbj.users — all rights on all users
cbj.users.profile — all rights on profiles only
cbj.users.profile.self — all rights on user’s own record

Looking more closely at the handle() function, we see that we are checking if the user has a claim string that is a substring of the one required on the route.

For example, a super admin will have the claim string “cbj”. This is found in the string “cbj.users” thereby granting her rights to all user records. A customer, however, will have “cbj.users.profile.self” — not a substring of “cbj.users”- and so they are blocked.

A small point that might have you confused looking at the above is that the “.” is just another character and is only used to help make it human-readable.

It’s also important to understand that this does not serve as tenancy code or help restrict users from seeing other user records. While it is certainly possible to try to do that via the claim strings, a much safer solution is to check each record against the user id that is also contained in the token. (We usually do this via the sql itself with a where clause.)

Hope that helps! Feel free to ask me about any part that isn’t clear about this or other aspects of how we create apis.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store