Multiple API Authentication using JWT in Laravel 9

Multiple API Authentication using JWT in Laravel 9
Spread the love

When building a REST API, sometimes we need multiple API authentication. In this tutorial, I will show you how to implement multiple authentications using JWT in Laravel Nine. In my previous blog, I have shown you how to build a restful API with JWT authentication. You can read this blog.

REST API CRUD with JWT Authentication in Laravel

Well, In this tutorial, I will show you everything from scratch. Let’s install a laravel 9 project using this command.

laravel new jwt_auth

Well, now create a database and connect it to your project. As well as create two models, two migrations files, and two controllers. Follow the below commands

php artisan make:model Product -mc
php artisan make:model Customer -mc  

Well, now install the JWT package for authentication. Run this command in your terminal to install the JWT package. Maybe this command will be changed later, still, they update this package.

composer require tomfordrumm/jwt-auth:dev-develop

This command adds a package in your composer.json file like this.

 "tomfordrumm/jwt-auth": "dev-develop"

Now go to the config/app.php file. In this file, inside the provider’s array add this line on code.

'providers' => [
    ...
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]  

Run the following command to publish the package config file. You should now have a config/jwt.php file that allows you to configure the basics of this package.

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

Now you have to create a JWT secret key. To do that run this command in your terminal then It will create a secret key in your .env file.

php artisan jwt:secret
// .env file
JWT_SECRET=cwreySsAfthkFUHCAJmlAIhNEqBshrGGOchMDyvN1u1sNz4id1VehVXT66c5o9Ti

Well, now you have to create custom guards for multiple authentications. So, go to the config/auth.php file and changed the code like below.

 'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin_api' => [
            'driver'   => 'jwt',
            'provider' => 'users',
        ],
        'customer_api' => [
            'driver'   => 'jwt',
            'provider' => 'customers',
        ],
    ],

Well, Inside the guards array the web is the default guard for web authentications. After the web guard, you have to create your custom guard like this code. Remember for API authentication your driver will be jwt. Now you have to add providers inside the provider’s array according to the guards.

'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'customers' => [
            'driver' => 'eloquent',
            'model' => App\Models\Customer::class,
        ],
    ],

Well, the users providers map the User model and the customers providers map the Customer model for authentication.

Well, now go to the User and Customer model and implement it with an interface. This interface is the JWT interface. Your code will be like below.

User.php

<?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;

    /**
     * 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',
    ];


    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    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 [];
    }
}

Customer.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Customer extends Authenticatable implements JWTSubject
{
    use HasFactory;

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

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


    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    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 [];
    }
}

Well, Our model part has been done. Now go to the routes/api.php file to add a couple of APIs. Add this bunch of code.

api.php

Route::prefix('products')->controller(ProductController::class)->group(function () {
    Route::middleware('auth:admin_api')->group(function () {
        Route::post('/', 'store');
        Route::post('update/{id}', 'update');
        Route::delete('/{id}', 'delete');
    });

    Route::middleware('auth:customer_api')->group(function () {
        Route::get('/', 'getAllProduct');
        Route::get('/{id}', 'show');
    });
});


Route::prefix('admin')->controller(AuthController::class)->group(function () {

    Route::post('login', 'login');
    Route::post('register', 'register');
    Route::middleware('auth:admin_api')->group(function () {
        Route::post('logout', 'logout');
        Route::post('me', 'me');
    });
});

Route::prefix('customer')->controller(CustomerAuthController::class)->group(function () {

    Route::post('login', 'login');
    Route::post('register', 'register');
    Route::middleware('auth:admin_api')->group(function () {
        Route::post('logout', 'logout');
        Route::post('me', 'me');
    });
});

Well, in this API file I added a middleware that I defined before. This middleware protected our API from unauthenticated users. Well now add this code to your controller. I defined two-controller before.

App\Http\Controllers\ProductController.php

public function getAllProduct()
    {
        try {
            $data = Product::get();
            return sendSuccessResponse($data);
        } catch (QueryException $e) {
            return sendErrorResponse("Something Went Wrong!", $e->getMessage(), 500);
        }
    }
    public function store($data = [])
    {
        try {
            Product::create($data);
            return sendSuccessResponse([], 'Data Created Successfully!', 201);
        } catch (QueryException $e) {
            return sendErrorResponse("Something Went Wrong!", $e->getMessage(), 500);
        }
    }
    public function show($id)
    {
        try {
            $data = Product::find($id);
            if ($data) {
                return sendSuccessResponse($data);
            } else {
                return sendErrorResponse([], 'Data Not found!', 404);
            }
        } catch (QueryException $e) {
            return sendErrorResponse("Something Went Wrong!", $e->getMessage(), 500);
        }
    }
    public function update($data = [], $id)
    {
        try {
            $data = Product::find($id)->update($data);
            return sendSuccessResponse($data, 'Data Updated Successfully!');
        } catch (QueryException $e) {
            return sendErrorResponse("Something Went Wrong!", $e->getMessage(), 500);
        }
    }
    public function delete($id)
    {
        try {
            $product =  Product::find($id);
            if ($product) {
                $product->delete();
                return sendSuccessResponse([], 'Data Deleted Successfully!', 200);
            }
        } catch (QueryException $e) {
            return sendErrorResponse("Something Went Wrong!", $e->getMessage(), 500);
        }
    }

Well, you may notice When I return a response I used a helper function. For this helper function, I defined I custom helper file. If you don’t know how to add a custom helper file in laravel application the read this blog.

Create Your Custom Helper Function in Laravel

Well, now Create a Controller for User and Customer authentications. I assume you are able to create the controller. Inside the controller, you code like below.

App\Http\Controllers\AuthController.php

 public function login()
    {
        $credentials = request(['email', 'password']);
        if (!$token = auth()->guard('admin_api')->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('admin_api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('admin_api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('admin_api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->guard('admin_api')->factory()->getTTL() * 60
        ]);
    }

    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|between:2,100',
            'email' => 'required|string|email|max:100|unique:users',
            'password' => 'required|string|min:6',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors()->toJson(), 400);
        }

        $user = User::create(array_merge(
            $validator->validated(),
            ['password' => bcrypt($request->password)]
        ));

        return response()->json([
            'message' => 'User successfully registered',
            'user' => $user
        ], 201);
    }

In this controller file, I added User or admin-related authenticated stuff. Remember You have to define a custom guard name when you access in Model or JWT.

App\Http\Controllers\CustomerAuthController.php

 public function login()
    {
        $credentials = request(['email', 'password']);
        if (!$token = auth()->guard('customer_api')->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth('customer_api')->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth('customer_api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth('customer_api')->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->guard('customer_api')->factory()->getTTL() * 60
        ]);
    }

    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|string|between:2,100',
            'email' => 'required|string|email|max:100|unique:customers',
            'password' => 'required|string|min:6',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors()->toJson(), 400);
        }

        $user = Customer::create(array_merge(
            $validator->validated(),
            ['password' => bcrypt($request->password)]
        ));

        return response()->json([
            'message' => 'Customer successfully registered',
            'user' => $user
        ], 201);
    }

In this file, I added customer authenticated stuff. Well, now time to check our API’s. You may notice that I define different middleware in different APIs, so Your token will be according to the Model.

Authenticated Customers Can get access to all products and single products.

GitHub: https://github.com/AR-Shahin/Laravel_Auth


Spread the love

About Anisur Rahman Shahin

Hello. My name is Shahin. I'm a tech enthusiast guy. Personally, I’m Optimistic and always in hurry kinda person. I'm a freelance web developer. I am working at Zakir Soft as Laravel Developer. My Portfolio website: https://devshahin.com/

View all posts by Anisur Rahman Shahin →

Leave a Reply

Your email address will not be published.