JSON APIs with Laravel: Part 5 - Authentication and Permissions cover image

JSON APIs with Laravel: Part 5 - Authentication and Permissions

Oliver Sarfas • August 27, 2019

programming laravel

This a part of my "JSON APIs with Laravel" series. If you've missed any of the previous episodes you can find them at the bottom of this article


Recap

Basic API is working as needed. But now, we need to add some basic authentication to give ourselves some control over who can do what.

Of course, this is entirely optional, so don't worry if you don't need it. Some APIs are public by design and will not require any of this.


Authentication

For our API authentication we will be using the jwt-auth package. To get started with it and get it installed, follow the Wiki found here

In a nutshell, this adds a new middleware called auth:api that we can use to protect our endpoints. For those interested, it's a custom Guard for Laravel that uses Token based Authentication against your user model.

Once installed, we'll need to add the new auth:api middleware to our controller / methods that we want to protect. In this instance, we're going to protect ALL of our API methods.

Protecting API endpoints

To add our auth:api middleware to the routes, we can either control them method-by-method, or just stick it on the controller. Equally, we can protect the Route declaration.

The chosen method is entirely down to personal preference. For this example, I'm going to put the middleware declaration in the Route declaration in the routes/web.php file.

We're going to change our file to look like the following;

# File; routes/web.php

<?php

Route::resource('contact', 'ContactController')->middleware([
    'auth:api',
]);

Now, whenever a request is made to any of our endpoints, you'll need to send Authorization headers with your request; preferably with a Bearer token method. There are alternative methods, using ?token={{token}} for instance.

Permissions

For granular permissions we're going to use Laravel Policies. So we'll make the ContactPolicy class, using the following command;

php artisan make:policy ContactPolicy --model=Contact

This gives us the app\Policies\ContactPolicy class, which we can give some granular permissions to. First, we'll register it in our AuthServiceProvider.

# File: app\Providers\AuthServiceProvider.php

<?php

namespace App\Providers;

use App\Contact;
use App\Policies\ContactPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Contact::class => ContactPolicy::class,
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
    }
}

Now our policy is mapped, we can run authorize() methods on our Contact model to ensure that the user has permission to do the action, before it is persisted/read to the database.

Granular permissions per method

For the sake of keeping this short, I'm going to show only one of our BREAD methods below. The same logic follows for the other 4, and you can extract this out to make your own concepts as well.

In this instance, we're going to restrict the Read method to only those who have an email address ending in @sarfas.codes.

To do so, go to the view(User $user, Contact $contact) method on our new ContactPolicy class.

We need to add the following logic to return a boolean, this boolean determines whether the action is allowed;

public function view(User $user, Contact $contact)
{
    return Str::endsWith($user->email, '@sarfas.codes');
}

Now, we just need to check if the user can do this, at the controller level. So let's go to the show() method in our controller and review it. Our show method needs to look like the following;

# File: app\Http\Controllers\ContactController.php

<?php

[...]

/**
 * Display the specified resource.
 *
 * @param  Request  $request
 * @param  Contact  $contact
 *
 * @return Response
 *
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function show(Request $request, Contact $contact)
{
    $this->authorizeForUser($request->user, 'view', $contact);
    return response()->json($contact);
}

[...]

This will check that the current user has sufficient permission to view the given Contact based on the Policy logic we've written. If it fails, the server will throw an AuthorizationException resulting in a HTTP 401 error.


Next Up? The end?

What do you want us to do next?. I'm at a crossroads on where to take this series right now 🤔 We've got a basic API, added some permissions and auth - so that should do for 90% of projects. You can get into rate limiting, verbose logging and the like, however they could realistically do with a series all of their own.

Let me know on twitter where you'd like this to go, or if there's any part of Laravel that you'd like me to go into detail on.


Previous Episodes

Episode 1

Episode 2

Episode 3

Episode 4