Integrate Passport to your Laravel API’s

Avinash Nethala
May 24 · 10 min read

Hello everyone, welcome back to justlaravel.com. I am back with another tutorial, here I will show you how to integrate Passport(a Laravel’s API authentication package) into your Laravel applications.

Here I will create 2 Laravel projects, one is an API and other is the API consumer app. It basically means I will create an API with Passport package integrated. A normal Laravel app where I call the API and show its data.

Let’s get started!

Laravel API

Consumer APP

First, let us create an API. In the terminal/command prompt run the following command to create a new Laravel project.

laravel new sampleAPI

Passport Install

Now, install the Passport package using composer,

composer require laravel/passport

Now, register the Passport service provider in the providers array of your config/app.php file.

'providers' => [
...
...
...
Laravel\Passport\PassportServiceProvider::class,
],

Now, run the following commands to setup passport, but before that setup database details in .env file ordatabase.php(app\config\database.php) file.

php artisan migrate

The migrate command creates some default Laravel tables and some new tables for authentication which comes from passport.

Now run,

php artisan passport:install

it creates some client id’s and secrets, which can be later used by consumer app to access the API.

Passport Configure

Here we need to make some configuration changes to our Laravel app so Passport can be used in the API authentication.

Now, in the User model(app\User.php) we need to add HasApiTokens trait from Passport package,

<?php
namespace App;
....
....
....
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
....
....
...
}

Next, we need to add some routes which passport uses, so in the AuthServiceProvider<code>(</code>app/Providers/AuthServiceProvider.php) add the below extra lines,

<?php

namespace App\Providers;

...
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
....
....
....
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}

And finally, in auth.php file(config/auth.php), I will set the API driver to passport.

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],

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

Generate data — Faker | DB Seeding

As this is not a real API and just for tutorial purpose, I can stuff with some dummy data.

Now I will generate some random fake data so we can fill up the API with some data. We can use an inbuilt Laravel package Faker to get us some data.

I will now create a copy of users table and fill that table with dummy users. Create a new details table by running the following SQL query.

-- Dumping structure for table lar_pas.details
CREATE TABLE IF NOT EXISTS `details` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `details_email_unique` (`email`)
)

Let’s create a seeder for details table, run the following command,

php artisan make:seed DetailsTableSeeder

n the Seeder file(database\seeds\DetailsTableSeeder.php), loop and create 250 random users, paste the following in the run() function of the seeder.

public function run() {
$faker = Faker\Factory::create();

for($i = 0; $i < 250; $i++) {
App\Detail::create([
'name' => $faker->name,
'email' => $faker->email,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
]);
}
}

Now, in the Database seeder file(database\seeds\DatabaseSeeder.php), call this DetailsTableSeeder

public function run()
{
$this->call(DetailsTableSeeder::class);
}

Now, we can run the migrate command which fills us the database.

php artisan migrate --seed

Routing | Getting info from DB

Now, lets create a route to the controller which gets all the user data, so in the api routes(\routes\api.php) place the following route.

Route::get('/users', 'DetailController@index');

Its time to create a new controller named DetailController as used in the above route.

php artisan make:controller DetailController

In the controller(app\Http\Controllers\DetailController.php), lets create a function and fetch all the data from user table.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Detail;

class DetailController extends Controller
{
public function index(){
$user = Detail::all();
return $user;
}
}

Now if we run the application(php artisan serve) and go to route(http://127.0.0.1:8000/api/users), it will display us all the users, but the thing here is everyone can see the details of the API, it just a normal app. An API needs authentication, so now let us use the passport package we have installed earlier.

Protect the routes with Passport

Let’s protect the route which we created earlier for displaying the users. Open api.php file(routes\api.php) and add middleware authentication to the route.

Route::get('users', 'UserController@index')->middleware('auth:api');

Now if we check the link(http://127.0.0.1:8000/api/users), we are now unable to access the data as it is protected with the passport and it needs some authentication.

Registration and Generating OAuth client_id and client_secret

So to access we need some client_id and client_secret, we have got some of these when I ran php artisan passport:install earlier and these are stored in a table called oauth_clients.

Now, if any user wants to access our API, we need them to register to the API and then generate those client_id and client_secret for them so they can access the API.

For registration view, I used the basic files generated that comes from Laravel’s auth scaffolding. So I ran, php artisan make:auth and removed all unnecessary files and kept only view files also removed its controllers and routes.

So make a new route in (routes\web.php) calling the registration view.

Route::get('/register', function(){
return view('auth.register');
});

So when the registration form is submitted, a new user is created and also an OAuth client id and secret are generated for that user.

The route(routes/web.php),

Route::post('/register', 'PassportController@register');

In the register function, I first save the user and then get its id and use it in oauth_clients table. So as am dealing with one or more insertions for an operation, I use a concept called DB Transactions, what this does is, it either inserts in both of the tables or none of them. As I don’t need any sort of incomplete operations here.

Assume a case where the user is created and got an error in creating a record in oauth_clients table, in this case, there is a problem, so to avoid such situations there is a concept called Transactions and Atomicity property of the transaction is what we use here.

So the basic flow of operation looks like,

public function register(Request $request)
{
// begin the transaction
DB::beginTransaction();

try {
...
...
....
try {
// Save User
}
catch(\Exception $e){
// if error in saving user, rollback - dont insert anything
DB::rollback();
}
try {
// Save in oauth Clients
}
catch(\Exception $e){
// if error in creating oauth_clients, rollback - dont insert anything
DB::rollback();
}

// if everything is fine, complete both the inserts(user, oauth_clients)
DB::commit();
} catch (\Exception $e) {
// if error any errors, rollback - dont insert anything
DB::rollback();
}

}

In the registration form, I check for some basic validations and then proceed to insert the user.

$validator = Validator::make($request->all(),[
'name' => 'required|string',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed'
]);

if ($validator->fails()) {
return Redirect::back()
->withErrors($validator)
->withInput();
}
else{
// insert user
}

Now, in the else part, I will create a new user, enclosing with try catch block for rollback scenario.

else{
try {
$user_save = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password)
]);
}
catch(\Exception $e){
DB::rollback();
$message = $e->getMessage();
return response()->json(['error' => 1, 'message' => $message]);
}
}

After the user is created, I will get the inserted id to use it the oauth_clients table.

$insertedId = $user_save->id;

Now, there is some data to be inserted in oauth_clients table.

Secret — A random 40 character string

$secret = Str::random(40);

Name — Any name

Password Client — As it is password access client the value should be ‘1’

Personal Access Client — It should be 0 as it is a password client and not personal

Redirect — A redirect URL

Revoked — whether the client revoked the access or not, it should be ‘0’ as we need access to the API

So the insert query for oauth_clients will look as follows,

try {
$oauth_clients = OAuthClient::create([
"user_id" => $insertedId,
"secret" => $secret,
"name" => "Password Grant",
"revoked" => 0,
"password_client" => 1,
"personal_access_client" => 0,
"redirect" => "http://localhost",
]);
}
catch(\Exception $e){
DB::rollback();
$message = $e->getMessage();
return response()->json(['error' => 1, 'message' => $message]);
}

After successful insertion of both queries, I can use DB commit and give the user the required client id and secret.

// user insert
// oauth clients insert

DB::commit();
return response()->json([
'message' => 'Successfully created user!',
'client_secret' => $secret,
'client_id' => $oauth_clients->id
], 201);

The final register function looks as below.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use DB;
use App\User;
use App\OAuthClient;
use Str;
use Validator;
use Redirect;

class PassportController extends Controller
{
public function register(Request $request)
{
DB::beginTransaction();

try {
$validator = Validator::make($request->all(),[
'name' => 'required|string',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed'
]);

if ($validator->fails()) {
return Redirect::back()
->withErrors($validator)
->withInput();
}
else{
try {
$user_save = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password)
]);
}
catch(\Exception $e){
DB::rollback();
$message = $e->getMessage();
return response()->json(['error' => 1, 'message' => $message]);
}
}
$insertedId = $user_save->id;
$secret = Str::random(40);
try {
$oauth_clients = OAuthClient::create([
"user_id" => $insertedId,
"secret" => $secret,
"name" => "Password Grant",
"revoked" => 0,
"password_client" => 1,
"personal_access_client" => 0,
"redirect" => "http://localhost",
]);
}
catch(\Exception $e){
DB::rollback();
$message = $e->getMessage();
return response()->json(['error' => 1, 'message' => $message]);
}
DB::commit();
return response()->json([
'message' => 'Successfully created user!',
'client_secret' => $secret,
'client_id' => $oauth_clients->id
], 201);
} catch (\Exception $e) {
DB::rollback();
// something went wrong
$message = $e->getMessage();
return response()->json(['error' => 1, 'message' => $message]);
}
}
}

We are done with the API part.

Consumer App

Now, let us create a consumer app, where this API data is fetched and displayed.

Create a new App

Let us create a new Laravel app,

laravel new consumerApp

Install GuzzleHttp

Here I use GuzzleHttp package to call the API just created. So let us install it. In the consumerApp root, run the following command to install it,

composer require guzzlehttp/guzzle

Authenticate the API and get the data

Now, let us authenticate the API and get data from the API. Here it is only one function so I will manage everything in a single function in the routes file(routes\web.php)

Assuming the API we created is running at http://localhost:8000

Route::get('/', function () {
$client = new GuzzleHttp\Client;
try {
$response = $client->post('http://localhost:8000/oauth/token', [
'form_params' => [
'client_id' => the_client_id_obtained_when_registered_to_API,
'client_secret' => 'the_client_secret_obtained_when_registered_to_API',
'grant_type' => 'password',
'username' => 'username_used_for_registering',
'password' => 'password_used_for_registering',
'scope' => '*',
]
]);

$auth = json_decode( (string) $response->getBody() );
$response = $client->get('http://localhost:8000/api/users', [
'headers' => [
'Authorization' => 'Bearer '.$auth->access_token,
]
]);
$details = json_decode( (string) $response->getBody() );
?>
...
}catch(..){
....
}
}

I will call the url http://localhost:8000/oauth/token with post method, this URL is one of the route created by the passport package.

As you see there are some form_params to be passed to it, client_id and client_secret are the one which obtained when registered to the API and the grant_type is password, and also we need to provide the username and password used for registering to the API, also scope should be passed as *

With the above call, we get an access_tokenand pass this access_token as a Header to the actual API call to get all the details.

$auth = json_decode( (string) $response->getBody() );
$response = $client->get('http://localhost:8000/api/users', [
'headers' => [
'Authorization' => 'Bearer '.$auth->access_token,
]
]);

Now, the obtained data is stored in some variable, so we can loop it and display the data accordingly.

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

Display the data

Here I will use a basic HTML table to display the data.

<table style="width:100%; border: 1px solid black;  border-collapse: collapse;">
<tr style="border: 1px solid black; border-collapse: collapse;">
<td>#</td>
<td>Name</td>
<td>Email</td>
</tr>
<?php foreach ($details as $detail) { ?>
<tr>
<td><?php echo $detail->id; ?></td>
<td><?php echo $detail->name; ?></td>
<td><?php echo $detail->email; ?></td>
</tr>
<?php } ?>
</table>

Run the app

So now lets run the application and see. As the API is running on default port 8000. Let us change the port and run it.

php artisan serve --port 8594

So now when we open http://localhost:8594/ we can see the data, which looks like the below image.

So that’s it, guys! We have successfully created an API with passport authentication and also consumed that API in another application.


justlaravel

justlaravel.com is website all about laravel where one can find useful tutorials with working demo, source code, video demonstration and more.

Avinash Nethala

Written by

An enthusiast Web Developer and Owner of justlaravel.com, programminginpython.com, mycodingcorner.com and other sites.

justlaravel

justlaravel.com is website all about laravel where one can find useful tutorials with working demo, source code, video demonstration and more.