Create an Oauth Authentication APIs with Laravel

When developing modern software applications, most times you will find yourself more and more needing an API that would serve as an interface for your application and database

Posted on February 7, 2018 in Laravel

When developing modern software applications, most times you will find yourself more and more needing an API that would serve as an interface for your application and database. In addition to creating the API, you would indeed want to keep it secure to avoid people who do not have access to your API from being able to access it. This is where laravel Passport comes in.

In this tutorial, I will show you how to set up a Laravel project to use Passport at it’s authentication driver. This tutorial assumes you are familiar with Laravel and have a basic understanding of OAuth.

Why Use Laravel Passport

If you have an API and you need authenticaion to secure the API then you need to use Laravel Passport to make sure this is secured. Also, if you have an API that you want others to consume, then Laravel Passport will provide you with everything you need to provide your consumer secure access to your API. For this demo we are going to have two applications. The client and the API. The client will be a sample application attempting to connect securely to our API to get some secure data from the API. The API will be, well, our make-believe API.

Setting up the API

For the sake of this tutorial let us assume we were creating todo list. Let’s create a new Laravel application, I prefer using Laravel installer, you are free to use any method that is most convenient for you.


   Composer create-project laravel/laravel/ todos 

Now that we are done, we have to create some migrations and seed the database tables with some sample data.


    cd todos
    php artisan make:model Todo --migration --controller

The artisan command above will so many wonderful things for us. It will first create a Todo model, create a migration for the todo table, and finally genereate a TodoController file for use all in on line.

Now let us quickly edit the migration created for us. Open the migration file just created for you in the ‘database’ directory and update the migration block.


    Schema::create('todos', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('user_id');
        $table->string('task');
        $table->boolean('done');
        $table->timestamps();
    });

Now, let us add a new route the “routes/api.php” file. We are adding it here bc we intend this to be an api accessible endpoint.


     Route::get('/todos', 'TodoController');

Now let us update the ‘TodoController’ to answer to our ‘api/todos’ endpoint. The endpoint will basically return all the todos on our system.


    // app/Http/Controllers/TodoController.php
    <?php

    namespace App\Http\Controllers;

    use App\Todo;

    class TodoController extends Controller
    {
        public function __invoke()
        {
            return Todo::all();
        }
    }

NOTE: If your controller only has one method and it responds to one route, you can use ‘__invoke’ method as the only method. This way, you do not have to specify the ‘@method’ when defining routes.

Now that we are done with the controller, we can create a seeder. This will fill up oure database with some sample data.


    $ php artisan make:seed UsersTableSeeder
    $ php artisan make:seed TodosTableSeeder

Now, we have the seeder files. Let us edit the seeder files so we can add the logic for the data we want to create. We will use Laravel’s awesome ‘model factories’ to generate the sample data.


    // database/seeds/UsersTableSeeder.php
    factory(App\User::class)->create(['email' => 'johndoe@scotch.io']);

    // database/seeds/TodosTableSeeder.php
    factory(App\Todo::class, 10)->create(['user_id' => 1]);

    // database/seeds/DatabaseSeeder.php
    $this->call(UsersTableSeeder::class);
    $this->call(TodosTableSeeder::class);

And finally, the last piece of the puzzle is creating the model factory entry. In your ‘database/factories/ModelFactory.php’ add the following block:


    $factory->define(App\Todo::class, function (Faker\Generator $faker) {
        return [
            'task' => $faker->sentence,
            'done' => rand(0,1),
        ];
    });

Now we need to make sure we have a database set up and the database details have been entered in our ‘.env’ file. After which we can then run the command:


     php artisan migrate --seed

Now, visting you endpoints URL ‘http://localhost:8080/api/todos’ should return a JSON object filled with sample todo items.

Setting up Laravel Passport for Authentication

Now that we have our API working, let us make it secure using Laravel Passport. In our todo API directory run the command to install Laravel Passport.


      composer require laravel/passport

Next, register the Passport service provider in the providers array of your ‘config/app.php’ configuration file:


    Laravel\Passport\PassportServiceProvider::class,

Now, you can run the following commands to fully install Laravel Passport to your application.


    php artisan migrate
    php artisan passport:install

You will notice, the ‘passport:install’ artisan command will return something similar to


    Encryption keys generated successfully.
    Personal access client created successfully.
    Client ID: 1
    Client Secret: OUA4IhQj1t3kDRuWZ6N7DQb9h1N3ccXpQw9HS2iz
    Password grant client created successfully.
    Client ID: 2
    Client Secret: oGhkm0EPSjqxJBMkaWNZ6lIuuZoby4ev787yW6cO

Passport has automatically installed two client applications for us, complete with ID and secret. Note down the client secret for client ID 1, we will need it later.

Next, you will need to add the ‘Laravel\Passport\HasApiTokens’ traits to your ‘App\User’ model.


<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}

Next, we need to register the Laravel passport routes. This is what provides us with authorization URLs for our clients to generate access tokens and authorize their requests.


// app/Providers/AuthServiceProvider.php
public function boot()
{
    $this->registerPolicies();

    Passport::routes();
}

Finally, in your ‘config/auth.php’ configuration file, set the driver option of the ‘api’ authentication guard to ‘passport’.


'guards' => [
    // ...

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

That’s all. We have installed Laravel passport completely. Now, let us move on to actually using Passport.

Protecting your endpoints with Laravel Passport.

At this point, our endpoint is still returning the entire list of todos whether we are authorized to see them or not, let’s fix that.

In the routes file, add the middleware ‘auth:api’ to the endpoints you want to protect. In our case, just the one endpoint.


// routes/api.php
Route::get('todos', 'TodoController')->middleware('auth:api');

Now when we hit the endpoint using Postman, we will get Unauthenticated error (401 unauthenticated)

NOTE: When visiting the endpoint using Postman, set the Header ‘Accept: application/json’ or Laravel Passport would never know it’s an API client and thus redirect to a ‘/login’ page for the web.

That is all. We are done with Laravel Passport.

Consuming the API from another application

Now that we have successfully protected our precious todo list, imagine if we wanted to access this list from a web application we own, how would we go about this?

Let first create client application first by typing following command in your command console.


$ mkdir -p todoconsumer
$ cd todoconsumer
$ echo '<?php require "vendor/autoload.php";' > index.php
$ echo '{}' > composer.json
$ composer require guzzlehttp/guzzle

We are going to use the password grant type to get an access token and use that to make a request to the protected endpoint. Edit index.php in the client application folder.


<?php

require "vendor/autoload.php";

$client = new GuzzleHttp\Client;

try {
    $response = $client->post('http://todos.dev/oauth/token', [
        'form_params' => [
            'client_id' => 2,
            // The secret generated when you ran: php artisan passport:install
            'client_secret' => 'fx5I3bspHpnuqfHFtvdQuppAzdXC7nJclMi2ESXj',
            'grant_type' => 'password',
            'username' => 'jyu@example.com',
            'password' => 'secret',
            'scope' => '*',
        ]
    ]);

    // You'd typically save this payload in the session
    $auth = json_decode( (string) $response->getBody() );

    $response = $client->get('http://todos.dev/api/todos', [
        'headers' => [
               'Authorization' => 'Bearer '.$auth->access_token,
        ]
    ]);

    $todos = json_decode( (string) $response->getBody() );

    $todoList = "";
        foreach ($todos as $todo) {
        $todoList .= "<li>{$todo->task}".($todo->done ? '✅' : '')."</li>";
    }

    echo "<ul>{$todoList}</ul>";

} catch (GuzzleHttp\Exception\BadResponseException $e) {
    echo "Unable to retrieve access token.";
}

So now, if we visit the url to our consumer, we should see the list of available todos in glorious unordered list.

Conclusion

Laravel Passport is a very powerful tool and this doesn’t even scratch the surface of what it is capable of doing. There are a myriad of features that make Laravel passport the best way to implement OAuth on your application.

You can download source code from Github.


comments powered by Disqus