How to Create Secure REST API in Laravel 8 with Passport

Novice developers often get stuck building REST APIs in Laravel; this tutorial offers you a simple and powerful technique to construct secure Laravel 8 Authentication REST APIs with Laravel 8 Passport package.

Laravel Passport is an OAuth2 server and API authentication package that is manageable and delightful to use. APIs usually rely on tokens to authenticate users and don’t manage session state amid requests. Laravel performs API authentication smoothly, adopting Laravel Passport, it offers a competent OAuth2 server implementation for your Laravel application swiftly.

By the end of this tutorial, you will have complete knowledge of working with Laravel environment from scratch. Over and above that, you will have the professional understanding of database, security, authentication, model, migration, and CREATE, READ, UPDATE & DELETE REST API testing with the postman.

REST + API

Roy Fielding introduced the idea of REST in the year 2000. It means Representational State Transfer and is an architectural style for distributed hypermedia systems. REST has its guiding principles to show you what I mean client-server, stateless, cacheable, uniform interface, layered system, and demand code.

API stands for application programming interface; it is a software intermediary that makes communication happen between two applications.

Fret not in through this tutorial; we’ll share some methods which will help you give exorbitant confidence to make HTTP requests with the help of REST API and communicate with the server to manage the CRUD operations securely.

Create Laravel Application

At first begin with Laravel application installation:

composer create-project laravel/laravel laravel-passport-rest-api-example --prefer-dist

Adding Database Details

Include your database name, user name and password in .env config file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db
DB_USERNAME=root
DB_PASSWORD=

Organize Passport in Laravel

Install Laravel passport package using composer command:

composer require laravel/passport

Run database migration:

php artisan migrate

Next, run the command to generate encryption keys for creating secure access tokens:

php artisan passport:install

Append the given below class for passport module in config/app.php file:

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

Inside your config/auth.php configuration file, you need to change the driver option of the api guard to passport:

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

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

Next, in your app/Models/User.php configuration file, import a HasApiTokens helper:

<?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',
    ];
}

Also, call the passport routes inside the boot method of AuthServiceProvider in app/Providers/AuthServiceProvider.php configuration file:

<?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();
    }
}

Model and Migration

You have to generate a Post model and migration configuration file:

php artisan make:model Blog -m

Put in table values for blog properties file in migrations/xxxxxxx_create_blogs_table:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateBlogsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('blogs', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedBigInteger('user_id');
            $table->text('name');
            $table->longText('detail');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users');  
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('blogs');
    }
}

Now that you have to move inside app/Models/Blog.php configuration file, what is more put in the given below code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Blog extends Model
{
    use HasFactory;
    protected $fillable = [
        'name', 
        'detail'
    ];     
}

Migrate the Blog model values:

php artisan migrate

Organize Passport Authentication Controller

Now, with the help of below command generate an authentication controller, REST API logic goes into it for login and sign up.

php artisan make:controller AuthController

Place the following code in AuthController.php controller file:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;

class AuthController extends Controller
{
    /**
     * Signin
     */
    public function signin(Request $request)
    {
        $data = [
            'email' => $request->email,
            'password' => $request->password
        ];
 
        if (auth()->attempt($data)) {
            $token = auth()->user()->createToken('LaravelPassportRestApiExample')->accessToken;
            return response()->json(['token' => $token], 200);
        } else {
            return response()->json(['error' => 'Unauthorised'], 401);
        }
    }

    /**
     * Signup
     */
    public function signup(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|min:2',
            'email' => 'required|email',
            'password' => 'required|min:4',
        ]);
 
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);
       
        $token = $user->createToken('LaravelPassportRestApiExampl')->accessToken;
 
        return response()->json(['token' => $token], 200);
    }
}

Next, generate the Blog controller file with below command:

php artisan make:controller BlogController

Now that, define the following functions for managing the Blog’s Create, Read, Update and Delete methods in BlogController.php controller file:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Blog;

class BlogController extends Controller
{
    public function index()
    {
        $blog = auth()->user()->blog;
 
        return response()->json([
            'success' => true,
            'data' => $blog
        ]);
    }
 
    public function show($id)
    {
        $blog = auth()->user()->blog()->find($id);
 
        if (!$blog) {
            return response()->json([
                'success' => false,
                'message' => 'Blog is not available! '
            ], 400);
        }
 
        return response()->json([
            'success' => true,
            'data' => $blog->toArray()
        ], 400);
    }
 
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            'detail' => 'required'
        ]);
 
        $blog = new Blog();
        $blog->name = $request->name;
        $blog->detail = $request->detail;
 
        if (auth()->user()->blog()->save($blog))
            return response()->json([
                'success' => true,
                'data' => $blog->toArray()
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Blog could not be added!'
            ], 500);
    }
 
    public function update(Request $request, $id)
    {
        $blog = auth()->user()->blog()->find($id);
 
        if (!$blog) {
            return response()->json([
                'success' => false,
                'message' => 'Blog could not be found!'
            ], 400);
        }
 
        $updated = $blog->fill($request->all())->save();
 
        if ($updated)
            return response()->json([
                'success' => true
            ]);
        else
            return response()->json([
                'success' => false,
                'message' => 'Blog could not be updated!'
            ], 500);
    }
 
    public function destroy($id)
    {
        $blog = auth()->user()->blog()->find($id);
 
        if (!$blog) {
            return response()->json([
                'success' => false,
                'message' => 'Blog could not be found!'
            ], 400);
        }
 
        if ($blog->delete()) {
            return response()->json([
                'success' => true
            ]);
        } else {
            return response()->json([
                'success' => false,
                'message' => 'Blog could not be deleted!'
            ], 500);
        }
    }
}

Go ahead and define the blog() function with Blog class, also add HasApiTokens inside the app/Models/User.php configuration 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 blog()
    {
        return $this->hasMany(Blog::class);
    }    
}

Build REST API Routes

Now to manifest authentication routes, you need to define the routes code in routes/api.php API routes file:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BlogController;
use App\Http\Controllers\AuthController;

/*
|--------------------------------------------------------------------------
| 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('signin', [AuthController::class, 'signin']);
Route::post('signup', [AuthController::class, 'signup']);
Route::middleware('auth:api')->group(function () {
    Route::resource('blog', BlogController::class);
});

Laravel 8 Passport API Demo

We are almost done with building Laravel REST API with Passport package, now start the application. Even more open the Postman application for testing the Passport authentication REST APIs:

php artisan serve

Signup with Laravel Passport REST API Example

http://localhost:8000/api/signup

Signup with Laravel Passport REST API Example

Signin REST API Demo
Add the token that was returned by signup API, add that as a Bearer token in authorization in postman app even more signin with email and password:

http://localhost:8000/api/signin

Signin REST API Demo

Testing Blog CRUD REST API

Your blog CRUD operations base URL:

http://localhost:8000/api/blog

The Header properties, for instance, accept and authorization (Bearer Token), needs to be set in the Postman app:

Create Blog

Create Blog

Get All Blogs

Get All Blogs

Get Blog

http://localhost:8000/api/blog/{id}

Update Blog

http://localhost:8000/api/blog/{id}

Update Blog

Post Delete API:

http://localhost:8000/api/blog/{id}

Conclusion

This foundational tutorial has shown you the right way of how to develop REST API in Laravel with Passport. Now, you have a basic understanding of creating, consuming, and testing the Laravel 8 Passport REST APIs.

Download code: https://github.com/remotestack377/laravel-passport-rest-api-example