Laravel 9 REST API with Passport Authentication Tutorial

Last updated on:  by Digamber
Do you want to know how to create a secure REST API using Passport in Laravel? If you have the same question, then with the conventional coherence about Laravel and Passport, we will learn the same thing.

In Laravel, you can take the holistic approach to build API. You won’t have to put intensive efforts; instead, you can give precedence to security. As far as security is concerned, Laravel Passport takes care of security and allows you to create Auth Token to provide authentication to users.

In this tutorial, we will learn to create robust, fast, and secure CRUD (CREATE, READ, UPDATE, DELETE) RESTful Authentication API with Passport Package in Laravel by following all the imperatives needed to be followed.

What is API (Application Programming Interface)?

API refers to the Application Programming Interface. It is a set of routines, protocols, and tools for creating software applications. An API interface makes communication possible between various software components.

In software development, API is a URL that handles the data for the web application through HTTP Requests GET, POST, UPDATE & DELETE, and manages the CRUD operations.

source: positronx

What is REST API?

Representational state transfer (REST) is a software architectural style that defines a set of constraints to be used for creating Web services. Web services that conform to the REST architectural style, called RESTful Web services, provide interoperability between computer systems on the Internet. RESTful Web services allow the requesting systems to access and manipulate textual representations of Web resources by using a uniform and predefined set of stateless operations. Other kinds of Web services, such as SOAP Web services, expose their own arbitrary sets of operations.
source: wikipedia

Install New Laravel Project

Let’s invoke the following command in the terminal to install a brand new Laravel application.

composer create-project laravel/laravel laravel-passport-auth --prefer-dist
Bash

Set Up Database

You must have either MAMP or XAMPP installed on your local development system, and the local server must be turned on.

This step explains how to make consensus between laravel and database, Incorporate the following code in .env file to establish the connection between both parties.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
.properties

If you are using MAMPP, then you might get the given below error while running migration. Please add the following line of code right after your database configuration inside the .env file.

DB_HOST=localhost;unix_socket=/Applications/MAMP/tmp/mysql/mysql.sock
Bash

Install Passport Package

On an impulse, the second step leads us to install the passport package through Composer package manager. Without further ado run the following command in your terminal.

composer require laravel/passport
Bash

Ideally, we have to use the default migration to create a new table in the MySQL database.

php artisan migrate
Bash

Next, generate token keys for strengthening the security and restrain hackers from deteriorating the security of our applications.

php artisan passport:install
Bash

Configure Passport Module

We need to focus on some nitty-gritty to configure the Passport package in the Laravel application. First, open app/Models/User.php file and include HasApiTokens trait inside the User model, as mentioned below.

<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
PHP

Next, open app/Providers/AuthServiceProvider.php file and register the registerPolicies() method inside the boot() function, It will evoke the required routes.

<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

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

Register the PassportServiceProvider class in providers array inside the config/app.php file:

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

Configure driver for the Passport, get inside the config/auth.php file and make the changes as shown below.

<?php
    return [
    ....
    ....
    
        'guards' => [
            'web' => [
                'driver' => 'session',
                'provider' => 'users',
            ],
    
            'api' => [
                'driver' => 'passport',
                'provider' => 'users',
            ],
        ],
    
    ....
    ....
]
PHP

Create Posts Model & Run Migration

To make the consensus between client and server, we will have to create the Post model by executing the below command.

php artisan make:model Post -m
Bash

After executing the above command, you will see the archetype of posts migration file in database/migrations/timestamp_create_posts_table. Here, you have to add some values to create the internal coherence using Model.

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedBigInteger('user_id');
            $table->text('title');
            $table->longText('description');
            $table->timestamps();
            $table->foreign('user_id')->references('id')->on('users');  
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
PHP

Next, create the app/Models/Post.php file and register the following values inside the $fillable array.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    use HasFactory;
    protected $fillable = [
        'title', 'description'
    ];    
}
PHP

Then, run the migration by using the below command.

php artisan migrate
Bash

Create a New Controller

Let us take another imperative in the consideration and, on the same impetus, execute the following command. It will create a new controller in our laravel app to create a login and registration REST API.

php artisan make:controller PassportAuthController
Bash

Controller is the quintessential file in Laravel application development. So, without further insert the given below code in PassportAuthController.php file.

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class PassportAuthController extends Controller
{
    /**
     * Registration
     */
    public function register(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|min:4',
            'email' => 'required|email',
            'password' => 'required|min:8',
        ]);
 
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);
       
        $token = $user->createToken('LaravelAuthApp')->accessToken;
 
        return response()->json(['token' => $token], 200);
    }
 
    /**
     * Login
     */
    public function login(Request $request)
    {
        $data = [
            'email' => $request->email,
            'password' => $request->password
        ];
 
        if (auth()->attempt($data)) {
            $token = auth()->user()->createToken('LaravelAuthApp')->accessToken;
            return response()->json(['token' => $token], 200);
        } else {
            return response()->json(['error' => 'Unauthorised'], 401);
        }
    }   
}
PHP

Before we move to next step, establish consensus between Post and User model. Gradually incorporate the following method inside the app/Models/User.php file.

<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    public function posts()
    {
        return $this->hasMany(Post::class);
    }    
}
PHP

Run command to create Post Controller.

php artisan make:controller PostController
Bash

Add the following code in PostController.php file.

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
class PostController extends Controller
{
    public function index()
    {
        $posts = auth()->user()->posts;
 
        return response()->json([
            'success' => true,
            'data' => $posts
        ]);
    }
 
    public function show($id)
    {
        $post = auth()->user()->posts()->find($id);
 
        if (!$post) {
            return response()->json([
                'success' => false,
                'message' => 'Post not found '
            ], 400);
        }
 
        return response()->json([
            'success' => true,
            'data' => $post->toArray()
        ], 400);
    }
 
    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required',
            'description' => 'required'
        ]);
 
        $post = new Post();
        $post->title = $request->title;
        $post->description = $request->description;
 
        if (auth()->user()->posts()->save($post))
            return response()->json([
                'success' => true,
                'data' => $post->toArray()
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Post not added'
            ], 500);
    }
 
    public function update(Request $request, $id)
    {
        $post = auth()->user()->posts()->find($id);
 
        if (!$post) {
            return response()->json([
                'success' => false,
                'message' => 'Post not found'
            ], 400);
        }
 
        $updated = $post->fill($request->all())->save();
 
        if ($updated)
            return response()->json([
                'success' => true
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Post can not be updated'
            ], 500);
    }
 
    public function destroy($id)
    {
        $post = auth()->user()->posts()->find($id);
 
        if (!$post) {
            return response()->json([
                'success' => false,
                'message' => 'Post not found'
            ], 400);
        }
 
        if ($post->delete()) {
            return response()->json([
                'success' => true
            ]);
        } else {
            return response()->json([
                'success' => false,
                'message' => 'Post can not be deleted'
            ], 500);
        }
    }
}
PHP

Define API Routes

Now, we will define API routes. Go to routes/api.php file and declare the foundational code.

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PassportAuthController;
use App\Http\Controllers\PostController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::post('register', [PassportAuthController::class, 'register']);
Route::post('login', [PassportAuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
    Route::resource('posts', PostController::class);
});
PHP

Test Laravel 9 Passport API

Eventually, we have completed all the foundational steps that were required to build REST API with Passport authentication in Laravel. Now, the time has come to test out the API, so run the following command to start the laravel app.

php artisan serve
Bash

We have to rely on Postman for testing our newly formed endpoints.

Register API:
You can test the Laravel Passport API for registering the user:

Please open the Postman app and Headers tab, define "Accept": application/json header value:

Set Header

http://localhost:8000/api/register
Markup

Laravel Passport User Registration

Login Passport API:
After sign up, copy the Bearer token, set into the Headers section in the Postman app. Check out the Laravel Passport Endpoint for logging-in:

http://localhost:8000/api/login
Markup

Passport Login API

Passport Post Create API:

http://localhost:8000/api/posts
Markup

To perform the CRUD operation, we need to set the correct authenticity. After successful registration and login, you will receive the access token. The manifestation of access token creates coherence with authorization, and It establishes secure communication with the server. You need to set this access token as a Bearer Token in the Authorization header.

'headers' => [
    'Accept' => 'application/json',
    'Authorization' => 'Bearer '. $accessToken,
]
.properties

Passport Post Create API

Laravel Passport Create API

Post List API:

http://localhost:8000/api/posts
Markup

Post List API

Show Single Post API:

http://localhost:8000/api/posts/{id}
Markup

Show Single Post API

Post Update API:

http://localhost:8000/api/posts/{id}
Markup

Post Update API

Post Delete API:

http://localhost:8000/api/posts/{id}
Markup

Post Delete API

The Bottom Line

Eventually, we have completed the Laravel 9 Passport API Tutorial. In this tutorial, we have shed light on every aspect needed to build secure REST APIs in Laravel.

We have gone through every foundation step and put everything at its place without falling into the trap of procrastination. This tutorial is useful for those who are new and want to try their hands to create a secure REST API with Passport in Laravel.

I have tried to shape things from my outlook on the entire journey, i haven’t been skeptical about anything. Anyhow, If i have skipped anything due to recklessness, you must download the full code of this tutorial from the GitHub.