w3resource

Laravel (5.7) API Authentication (Passport)

For this demo we are going to have two applications. The client (or consumer) 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

Setup a new laravel application:

$ laravel new 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 below will do so many wonderful things for us. It will first create a Todo model, create a migration for the todos table, and finally generate a TodoController file for us all in one 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 to the routes/api.php file. We are adding it here because we intend this to be an api accessible endpoint.

// routes/api.php
Route::get('/todos', 'TodoController');

Now let's update the TodoController to answer to our /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();
    }
}

Now that we are done with the controller, we can create a seeder. This will fill up our 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 to generate the sample data.

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

// 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, visiting your endpoints URL http://todos.dev/api/todos should return a JSON object filled with sample todo items.

Setting up the consumer

Create a new directory somewhere in your development machine.

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

Make sure the consumer is accessible via HTTP. That's all, we will get back to this consumer app in a bit.

Setting up Laravel Passport for Authentication

Now that we have our API working, let us make it secure using Laravel Passport. In your 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 this later.

Next, you will need to add the Laravel\Passport\HasApiTokens trait 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, lets 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 an Unauthenticated error.

That is all. We are done with Laravel Passport. You can actually stop reading or continue to see how a sample client would consume this API.

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 us return to our consumer application. 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 the index.php file in the consumer:

<?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' => '[email protected]',
            '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.";
}

Previous: Laravel (5.7) Hashing
Next: Laravel (5.7) Email Verification Tutorial Example



Follow us on Facebook and Twitter for latest update.