Apply Multiple Authentication Guards in Laravel 10 with Jwt and Postman

Amirnagy
6 min readOct 13, 2023

Introduction:

In the world of development, there are some handy tools and tricks that make building secure and user-friendly websites a breeze. Think of Laravel as a powerful tool that helps create web applications and backends for mobile applications. It’s like having a super-smart assistant for building applications.

Now, imagine you’re making a door to your website or mobile, and you want to control who gets in and what they can do inside. That’s where guards come in — they help you manage access and keep your website safe.

But how do you make sure your door is working correctly? That’s where Postman comes into play. Postman is like a special tester for your website. It helps you check if your website’s door opens and closes as it should.

In this guide, we’ll show you how to use Laravel, Postman, and a thing called RESTful API (which is just a fancy way to say “website language”) to create secure and working websites. By the end of this journey, you’ll be able to make apps that are safe and easy to use. So, let’s start building and testing together

Let's get start

Now we need to install the Laravel 10 fresh application using the below command, Open your command prompt and run the below command :

composer create-project laravel/laravel GuardApp

Setup Database Credentials

We go to the project .env file and set up database credentials.

DB_CONNECTION=mysql 
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=here your database name here
DB_USERNAME=here database username here
DB_PASSWORD=here database password here

first I will use tymon/jwt-auth for JWT implementation.

with this command,

composer require tymon/jwt-auth

you can read more about installing this package here

https://jwt-auth.readthedocs.io/en/develop/laravel-installation/

Now we will generate a Model and Migration

With this command, we will create a model and migration for table admins

php artisan make:Model Admin -m

admins migration file will be empty but we will fill it by name email, and so on like the user migration file.

<?php

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

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->timestamps();
});
}

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

The model file of Admins

note that

we will apply a guard with protected $guard = ‘admin’

in the Admin model, we will guard it with admin and User it by api

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User ;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Admin extends User implements ShouldQueue , JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/

protected $guard = 'admin';

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',
'password' => 'hashed',
];


public function getJWTIdentifier()
{
return $this->getKey(); // Assuming your admin model has a primary key called "id".
}

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

by default, we have a user and admin model and migration so we didn’t need to make it again but we will refactor things for JWT like admin by implementing JWTSubject

The Migration User

<?php

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

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}

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

The Model of User

<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

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

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $guard = ["api"];

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',
'password' => 'hashed',
];


/**
* 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 [];
}

now will migrate our files to by tables in phpmyadmin by

php artisan migrate          

now we will apply guards on for restful in our app

in-app we will modify config/auth.php to add an admins guard that will be responsible for the admins table

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

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


'admin' => [
'driver' => 'jwt',
'provider' => 'admins',
],
],

and modify the provider that is responsible for retrieving data from the database we will add an admin provider ‘driver’ => ‘eloquent’, which means we will use eloquent ORM, and model is the model of our table

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

we will make a controller for admin auth and user auth to make our code clean and I add this function response in my controller because I will use it in both controllers

public function finalResponse($message = "success",
$statusCode = 200,
$data = null,
$pagnation = null,
$errors = null) : JsonResponse
{
return response()->json([
"message" => $message,
"data" => $data,
'pagination' => $pagnation,
'errors' => $errors
],$statusCode);
}

now create controllers

php artisan make:controller AuthUser 

php artisan make:controller AuthAdmin

in our user controller, we will add two function

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Tymon\JWTAuth\Exceptions\JWTException;

class AuthUser extends Controller
{

public function loginUser(Request $request)
{
$credentials = $request->only('email', 'password');
try {
$token = auth('api')->attempt($credentials);
if (!$token) {
return response()->json(['success' => false, 'error' => 'Some Error Message'], 401);
}
} catch (JWTException $e) {
return response()->json(['success' => false, 'error' => 'Failed to login, please try again.'], 500);
}
return $this->finalResponse($token);
}


public function registerUser(Request $request)
{
$credentials = $request->only('email', 'password');
$request->merge(['password' => Hash::make($request->password)]);
$username = explode('@', $request->email)[0];
$user = User::create([
'name' => $username,
'username' => $username,
'email' => $request->email,
'password' => $request->password,
]);
return response()->json('success' . ' ' . $user->name . ' ' . $user->email . ' ');
}


}

in our admin controller auth, we will add also two function

<?php

namespace App\Http\Controllers;

use App\Models\Admin;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Tymon\JWTAuth\Exceptions\JWTException;


class AuthAdmin extends Controller
{

public function registerAdmin(Request $request)
{
$credentials = $request->only('email', 'password');
$request->merge(['password' => Hash::make($request->password)]);
$username = explode('@', $request->email)[0];
$user = Admin::create([
'name' => $username,
'username' => $username,
'email' => $request->email,
'password' => $request->password,
]);
return response()->json('success');
}



public function loginAdmin(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (!$token = auth()->guard('admin')->attempt($credentials)) {
return response()->json(['success' => false, 'error' => 'Some Error Message'], 401);
}
} catch (JWTException $e) {
return response()->json(['success' => false, 'error' => 'Failed to login, please try again.'], 500);
}
return $this->finalResponse($token);
}


}

now will make our routes but I make a file route for the user and one for the admins in our RouteServiceProvider.php

Route::middleware('api')
->prefix('api/admin')
->group(base_path('routes/admin.php'));

now add admin.php manually in the directory routes in it i add

<?php

use Illuminate\Http\Request;

use App\Http\Controllers\AuthAdmin;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TestAuth;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API admins routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/


Route::post('register/admin',[AuthAdmin::class, 'registerAdmin']);
Route::post('login/admin',[AuthAdmin::class, 'loginAdmin']);


Route::middleware(['auth:admin'])->group(function () {
Route::get('view',[TestAuth::class, 'index']);
});

now add routes for user in api.php

<?php

use Illuminate\Http\Request;
use App\Http\Controllers\AuthUser;
use App\Http\Controllers\AuthAdmin;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AdminRegisteration;
use App\Http\Controllers\TestAuth;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/


Route::post('register/user',[AuthUser::class, 'registerUser']);
Route::post('login/user',[AuthUser::class, 'loginUser']);


now you can import our collection in Postman and test it on your local host by using this link

https://api.postman.com/collections/24212283-1b12e999-626a-4dab-8983-9806fba5bd3f?access_key=PMAT-01HCMW8BYJKCK0FHTX8CW284CD

--

--