Simplified Laravel 9 REST API Tutorial using JWT Authentication

Last Updated: 05 Jan, 2023

In this tutorial, you will learn step-by-step process of how to authenticate REST API application in Laravel 9 using JSON Web Token. In this comprehensive tutorial, you will use tymon/jwt-auth composer package that will help us to implement JWT authentication process.

What is JSON Web Token?

JWT also refered as JSON Web Token. JWT is an open standard that is widely used to share secure infromation between client and server. JSON Web Token contains base64 encoded JSON objects along with a set of claims. Each JWT is signed by using a cryptographic algorithm which is responsible to make sure that the claims cannot be modified once the token is issued.

What is JSON?

JSON is also refered as JavaScript Object Notation. JSON is a text-based format that is widely used for transmission of data amongs web applications. JSON stored information in very simple and easily accessible manner. Currently, JSON is very popular and used by many pupular programming languanges.

How to Create Laravel 9 Rest API CRUD Application using JWT Authentication?

You have to just follow this simplified step-by-step process from scratch to create a simple and comprehensive REST APIs application powered by JWT (JSON Web Token) authentication:

Step 1: Create Fresh Laravel 9 Application

In first step, let's create a fresh laravel 9 application. Open up your terminal and execute the below command:

composer create-project laravel/laravel l9restjwt

Step 2: Configure Database Credentials in .env file

Next, let's open .env file and update following configurations as follows:

DB_DATABASE=l9restjwt
DB_USERNAME={yourdbusername}
DB_PASSWORD={yourdbpassword}

Step 3: Install JWT Authentication Package and Configure it

We will be using tymon/jwt-auth composer package to authenticate the our REST APIs. This is a third-party package that allows us to authenticate application users using JSON Web Token in Laravel securely. Please run below command in terminal to install this package:

composer require tymon/jwt-auth:^1.0

Once you have successfully install JWT Auth package, let's configure the necessary stuffs. Register the JWT auth providers in your config/app.php file as follows:

config/app.php

'providers' => [
    ...
    ...
    ...
    /*
     * Package Service Providers...
     */
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
'aliases' => Facade::defaultAliases()->merge([
    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
    'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
])->toArray(),

Next, you will have to publish the JWT Auth package’s configuration. Run below command in your terminal that will copy JWT Auth files from vendor directory to config/jwt.php file:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Next, you have to run the below command to generate a JWT SECRET KEY that will be responsible to handle the JWT token encryption:

php artisan jwt:secret

Upon successful execution of this command, a secret key will be genrated inside your .env file:

JWT_SECRET={your_jwt_secret_key}

Step 4: Modify User Model

In this step, we have to do little bit modification to User model which is already provided by Laravel when you install it freshly. This model is used for authentication purpose. Yo need to import necessary JWT stuffs into User model to implement the jwt-auth package.

You have to import Tymon\JWTAuth\Contracts\JWTSubject inside User model and define below two methods in it:

1. getJWTIdentifier() : This method will help you to get get the JWT identifier that is stored in the subject claim of the JWT.

2. getJWTCustomClaims() : This method will return a key value array that contains any custom claims to be added to the JWT.

Open app/Models/User.php model and copy below given code in it:

<?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\Sanctum\HasApiTokens;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
}

Step 5: Create Country Model and migration

Next, we have to create Country model and migration files using below command:

php artisan make:model Country -m

Above command will generate a model file named Country.php and a migration file named database/migrations/*_create_countries_table.php. Open the migration file and copy below code in it:

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('countries', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('code');
            $table->string('capital');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('countries');
    }
};

Next, open app\Models\Country.php and copy below code in it:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Country extends Model
{
    use HasFactory;
    
    protected $fillable = [
        'name',
        'code',
        'capital',
    ];
}

Now, let's run the below command to create the tables in MySQL DB:

php artisan migrate

Step 6: Configure Authentication Guards

Next, you have to configure auth guard for JWT to secure the authentication process. Laravel provides us with the session driver to protect the guards. We will be using the JWT driver for API guard. Open your config/auth.php file and copy below code in it.

<?php
return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
            'hash' => false,
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
    ],
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
    'password_timeout' => 10800,
];

Step 7: Create and Set Up Auth Controller

In this step, we have to create AuthController and need to define necessary methods and logics. Let's run below command in terminal that will create app/Http/Controllers/AuthController.php file.

php artisan make:controller Api/AuthController

Next, open the app/Http/Controllers/AuthController.php and copy below code in it:

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\Models\User;

class AuthController extends Controller
{
    public function __construct() {
        $this->middleware('auth:api', ['except' => ['login', 'register']]);
    }

    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required|string|min:8'
        ]);
        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ], 422);
        }
        if (! $token = auth('api')->attempt($validator->validated())) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Credentials',
            ], 400);
        }
        return $this->respondWithToken($token);
    }

    public function register(Request $request) 
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|min:3|max:50',
            'email' => 'required|string|email|max:100|unique:users',
            'password' => 'required|string|confirmed|min:8',
        ]);
        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ], 401);
        }
        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();
        return response()->json([
            'status' => true,
            'message' => 'User successfully registered',
            'user' => $user
        ], 201);
    }

    public function logout(Request $request) 
    {
        try {
            auth('api')->logout();
            return response()->json([
                'status' => true,
                'message' => 'Successfully logged out'
            ]);
        } catch (Exception $e) {
            return response()->json([
                'status' => false,
                'message' => 'Sorry, cannot logout'
            ], 500);
        }
    }

    public function userProfile(Request $request) 
    {
        return response()->json([
            'status' => true,
            'message' => 'User found',
            'data' => auth('api')->user()
        ], 200);
    }

    public function refresh(Request $request) 
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    protected function respondWithToken($token)
    {
        $minutes = auth('api')->factory()->getTTL() * 6000;
        $timestamp = now()->addMinute($minutes);
        $expires_at = date('M d, Y H:i A', strtotime($timestamp));
        return response()->json([
            'status' => true,
            'message' => 'Login successful',
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_at' => $expires_at
        ], 200);
    }
}

Step 8: Create CountryController

In this step, we have to create CountryController by running below command in your terminal that will create app/Http/Controllers/CountryController.php file.

php artisan make:controller Api/CountryController

Next, open the app/Http/Controllers/CountryController.php and copy below code in it:

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\Models\Country;

class CountryController extends Controller
{
    public function __construct() {
        $this->middleware('auth:api');
    }

    public function index()
    {
        $countries = Country::all();
        return response()->json([
            "status" => true,
            "message" => "List of Countries",
            "data" => $countries
        ]);
    }

    public function store(Request $request)
    {
        $request_data = $request->all();
        $validator = Validator::make($request_data, [
            'name' => 'required',
            'code' => 'required',
            'capital' => 'required'
        ]);
        if ($validator->fails()) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ]);
        }
        $country = Country::create($request_data);
        return response()->json([
            "status" => true,
            "message" => "Country created successfully.",
            "data" => $country
        ]);
    }

    public function show(Country $country)
    {
        if (is_null($country)) {
            return response()->json([
                'status' => false,
                'message' => 'Country not found'
            ]);
        }
        return response()->json([
            "success" => true,
            "message" => "Country found.",
            "data" => $country
        ]);
    }

    public function update(Request $request, Country $country)
    {
        $request_data = $request->all();
        $validator = Validator::make($request_data, [
            'name' => 'required',
            'code' => 'required',
            'capital' => 'required'
        ]);
        if($validator->fails()){
            return response()->json([
                'status' => false,
                'message' => 'Invalid Inputs',
                'error' => $validator->errors()
            ]);      
        }
        $country->name = $request_data['name'];
        $country->code = $request_data['code'];
        $country->capital = $request_data['capital'];
        $country->save();
        return response()->json([
            "status" => true,
            "message" => "Country updated successfully.",
            "data" => $country
        ]);
    }

    public function destroy(Country $country)
    {
        $country->delete();
        return response()->json([
            "status" => true,
            "message" => "Country deleted successfully.",
            "data" => $country
        ]);
    }
}

Step 9: Create Authentication and CRUD Routes

API routes are stored in routes/api.php file and served this file. All APIs routes are prfixed with api/ and authentication routes are denoted by auth/. You have to create a resource route for CRUD operation on Country table.

So, open your routes/api.php file and copy below code in it:

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\CountryController;

Route::group(['middleware' => 'api'], function () {
    Route::group(['prefix' => 'auth'], function () {
        Route::post('/login', [AuthController::class, 'login']);
        Route::post('/register', [AuthController::class, 'register']);
    });
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::post('/refresh', [AuthController::class, 'refresh']);
    Route::get('/profile', [AuthController::class, 'userProfile']);  
    Route::resource('/countries', CountryController::class);  
});

Step 10: Run Laravel Development Server

Now, let's start the application server by using below command in terminal:

php artisan serve

Once development server is started, you can test your application with postman.

Step 11: Test your REST API CRUD Application using Postman

We will be using POSTMAN to test our CRUD REST API application. Open postman application and test your APIs one-by-one by following below instructions.

Registration API (http://localhost:8000/api/auth/register)

First of all, you need to register a new user by making POST request to /api/auth/register API. In the request body select form-data and fill in all the required parameters (name, email, password, password_confirmation). Please refer the below screenshot:

Simplified Laravel 9 REST API Tutorial using JWT Authentication

Login API (http://localhost:8000/api/auth/login)

Next, test login functionality by making POST request to the /api/auth/login API. Fill in required parameters (email and password) under form-data in request body. On successful, login API will return a JWT access token along with token type and token expiration time, etc. Below screenshot shows the same:

Simplified Laravel 9 REST API Tutorial using JWT Authentication
Once you have got the API Access Token, you can easily call other APIs (User Profile, Token Refresh and Logout) by providing the access token in request header.

CRUD APIs for Countries Table

Country CREATE API (http://localhost:8000/api/countries)

Next, call the POST REST API to perform CREATE operation on Country model. Below screenshot shows the POST api call:

Simplified Laravel 9 REST API Tutorial using JWT Authentication

Country READ API (http://localhost:8000/api/countries)

Next, call the GET REST API to perform READ operation on Country model. Below screenshot shows the GET api call:

Simplified Laravel 9 REST API Tutorial using JWT Authentication

Country UPDATE API (http://localhost:8000/api/countries/1)

Next, call the UPDATE Rest API to perform UPDATE operation on Country model. Below screenshot shows the UPDATE api call:

Simplified Laravel 9 REST API Tutorial using JWT Authentication

Country DELETE API (http://localhost:8000/api/countries/3)

Next, call the DELETE REST API to perform DELETE operation on Country model. Below screenshot shows the DELETE api call:

Simplified Laravel 9 REST API Tutorial using JWT Authentication

Thank You, Please Share.

Recommended Posts

Simplified Laravel 9 REST API Tutorial using Sanctum  Authentication

Simplified Laravel 9 REST API Tutorial using Sanctum Authentication

In this simplified tutorial, you will learn the simplest way to create a REST API application in Laravel 9 that will be authenticated using Laravel Sanctum.

Simplified Laravel 9 REST API Tutorial using Passport  Authentication

Simplified Laravel 9 REST API Tutorial using Passport Authentication

In this simple tutorial, you will learn all the step-by-step process of authenticating Laravel 9 REST API application using Laravel Passport.

Simplified Laravel 9 Cashfree Integration Tutorial for Beginners

Simplified Laravel 9 Cashfree Integration Tutorial for Beginners

In this awesome tutorial, we are going to learn the simplest way of integrating Cashfree Payment Gateway inside Laravel 9 application.