Spread the love

JWT (Json Web Token) is used to create the authentication between two parties client and server that means it creates a secure information transactions between two parties. In the process of php-open-source-saver/jwt-auth token it creates a token claims and generates a token behalf of these claims then it verify on each request before processing the activities on application whether its authenticated or not.

Laravel by default uses passport authentication for Rest APIs but we can override it in config file to use the jwt token. Jwt tokens are easy to use and can easily maintain the authenticity of request by JSON token in request.

JWT tokens are signed with HMAC algorithm or private/public key. JWT tokens contains main three parts Header, Payload, signature separated by DOT(.)

In this article of Implement JWT Auth in Laravel I will use PHP php-open-source-saver/jwt-auth package to create jwt token in laravel 9. This package has several methods to attempt, check , refresh and many more.

In this example we will create a simple example to create the json token the library and when a user send a request for login by sending username and password if authentication successful the we will create the token and response back with the token to access the server using auth token. Client can save token in local storage or cookies for later use.

Tokens are time based means token will expire after a time period but we can refresh the token regularly to use it for long time.

Let’s start the tutorial of Implement JWT Auth in Laravel 9 with simple step by step

Step 1: Create a fresh laravel project

Open a terminal window and type below command to create a new project

composer create-project --prefer-dist laravel/laravel blog

You can also read this to start with new project

Step 2 : Install the package

I assume the you have already installed the laravel and basic connection of it like database connection and composer.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-jwt
DB_USERNAME=root
DB_PASSWORD=

Now install the package using composer in laravel root directory, open the terminal in laravel root directory and run below command to install php-open-source-saver/jwt-auth the package. This package has good stars on github and has lot of fan base of artisans

composer require php-open-source-saver/jwt-auth 

Step 3 : Publish Vendor and Generate Secret Key

Now, publish the configuration files of package

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

this will create a a file in config/jwt.php where you can configure the jwt token configurations.

and generate token using below command

php artisan jwt:secret

this will update a key JWT_SECRET in .env file as below

JWT_SECRET=generated_token

Step 4 : Generate user migration

If you have just installed a fresh project then Create the default table of users in database using the artisan command

php artisan migrate

Now, for an example to access the articles by only authenticated user so i am creating here a table using migration and then i am going to fill the data in it, so create model and migration

php artisan make:model Article -m

this will generate the model and migration file

<?php

namespace App\Models;

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

class Article extends Model
{
    use HasFactory;
    
}

and migration file database/migrations/timestamp_create_articles_table.php

<?php

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

return class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
     public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('email')->unique();;
            $table->string('title');
            $table->string('body')->nullable();
            $table->string('image');
            $table->timestamps();
        });
    }

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

and then migrate the migration

php artisan migrate

Step 5 : Configure AuthGuard

By default laravel uses its own auth for Rest APIs so we need to change the AuthGuard to Jwt in the config/auth.php


    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

   

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

To

'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],


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

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

    ],

Here we changed default auth to api and also added new guard

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

Step 6 : Update User Model

To use the jwt we need to implement two methods in app\Models\User.php class and add getJWTCustomClaims() and getJWTIdentifier() so update the User model as following

<?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 PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;

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

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

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

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

and also create a article table and migration so we can access the articles only after login

php artisan make:model Article -m

Above command will create two files one is migration and other one is model. open migration file which is located at database/migrations/timestamp_create_articles_table.php and edit the schema as below

  public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('email')->unique();;
            $table->string('title');
            $table->string('body');
            $table->timestamps();
        });
    }

Run migration using artisan command in command line

php artisan migrate

Output of above command

Migrating: 2021_11_27_112800_create_articles_table
Migrated:  2021_11_27_112800_create_articles_table (45.63ms)

Now create seeder in database/seeders/DatabaseSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

// Import DB and Faker services
use Illuminate\Support\Facades\DB;
use Faker\Factory as Faker;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $faker = Faker::create();

    	for ($i=0;$i<=100;$i++) {
            DB::table('articles')->insert([
                'title' => $faker->name,
                'body' => $faker->text,
                'email' => $faker->email,
                'updated_at' =>$faker->datetime,
                'created_at' => $faker->datetime              
            ]);
        }
        
    }
}

Run Seeder in command line

php artisan db:seed

Step 3 : Create controller

Let’s create a controller and add a methods login, register, refresh and logout

php artisan make:controller AuthController

and add the below code

<?php

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use App\Models\User;

class AuthController extends Controller
{

     
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
            'password' => 'required|string',
        ]);
        $credentials = $request->only('email', 'password');

        $token = Auth::attempt($credentials);
        if (!$token) {
            return response()->json([
                'status' => 'error',
                'message' => 'Unauthorized',
            ], 401);
        }

        $user = Auth::user();
        return response()->json([
                'status' => 'success',
                'user' => $user,
                'authorisation' => [
                    'token' => $token,
                    'type' => 'bearer',
                ]
            ]);

    }

    public function register(Request $request){
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        $token = Auth::login($user);
        return response()->json([
            'status' => 'success',
            'message' => 'User created successfully',
            'user' => $user,
            'authorisation' => [
                'token' => $token,
                'type' => 'bearer',
            ]
        ]);
    }

    public function logout()
    {
        Auth::logout();
        return response()->json([
            'status' => 'success',
            'message' => 'Successfully logged out',
        ]);
    }

    public function refresh()
    {
        return response()->json([
            'status' => 'success',
            'user' => Auth::user(),
            'authorisation' => [
                'token' => Auth::refresh(),
                'type' => 'bearer',
            ]
        ]);
    }
    public function articles(){
        return response()->json([
            'status' => 'success',
            'data' => \App\Models\Article::paginate()
            
        ]);
    }

}

Here in above code we have used four methods

Function login : It accepts email and password and if authentication successful then return the user with token.

Function register : To create a new user with email, name and password then response with token.

Function refresh : To refresh the old token.

Function logout : To invalidate the token and logout the user.

Function articles : List of articles with logined user.

Step 4: Create routes in routes/api.php

Create route to login, register, refresh and logout

routes/api.php

<?php

use App\Http\Controllers\ArticleController;
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;
 

Route::controller(AuthController::class)>group(function () {
   Route::post('login', 'login');
   Route::post('register', 'register');
});
Route::controller(AuthController::class)->middleware("auth:api")->group(function () {
    Route::post('logout', 'logout');
    Route::post('refresh', 'refresh');
    Route::get('articles', 'articles');
});

I

Postman Screenshot:

Response without login:

Accessing apis without login

Note : Make sure to pass header accept : application/json for proper json response in case of unauthntication.

Also Read : How to create multiple size thumbs from image in laravel 8 ?

Leave a Reply