Multi-Guard Authentication with Laravel Fortify

Multi-Guard Authentication with Laravel Fortify

Laravel fortify is a starter kit that provides a full authentication implementation.By using different guards for different users like for admin and users ,Guard helps us to manage access by validating every request for each authenticated user.If you want to read more go to Official Laravel Documentation

Database Setup and User Models

We need a new model and migration for admins and rest all users will be stored in a users tables.
php artisan make:model Admin -m
Our admins table.
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->rememberToken(); $table->timestamps(); });
Admin model
namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class Admin extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; }
Make sure to connect to your Database. #### Setting Up Laravel Fortify
composer require laravel/fortify php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
Now, we should need to migrate
php artisan migrate
#### Fortify Provider Setup Now register FortifyServiceProvider within the providers array of your application's config/app.php
App\Providers\FortifyServiceProvider::class
Navigate to App\Providers inside FortifyServiceProvider
namespace App\Providers; use App\Actions\Fortify\CreateNewUser; use App\Actions\Fortify\ResetUserPassword; use App\Actions\Fortify\UpdateUserPassword; use App\Actions\Fortify\UpdateUserProfileInformation; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\ServiceProvider; use Laravel\Fortify\Fortify; class FortifyServiceProvider extends ServiceProvider { /** * Register any application services. */ public function register(): void { if (request()->is('admin/*')) { config()->set('fortify.guard', 'admin'); config()->set('fortify.home', '/admin/home'); } } /** * Bootstrap any application services. */ public function boot(): void { Fortify::createUsersUsing(CreateNewUser::class); Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class); Fortify::updateUserPasswordsUsing(UpdateUserPassword::class); Fortify::resetUserPasswordsUsing(ResetUserPassword::class); RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->email . $request->ip()); }); RateLimiter::for('two-factor', function (Request $request) { return Limit::perMinute(5)->by($request->session()->get('login.id')); }); Fortify::registerView(function () { Fortify::registerView('auth.register'); }); Fortify::loginView(function () { return view('auth.user-login'); }); } }

Configuring Multi-Guards

Open your config/auth.php file and define multiple guards under the guards array
return [ /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], /* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider. | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session" | */ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], ], /* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], 'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], /* |-------------------------------------------------------------------------- | Password Confirmation Timeout |-------------------------------------------------------------------------- | | Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ 'password_timeout' => 10800, ];

Middleware Setup

Now, let's modify middleware.Goto your Authenticate.php inside Middleware
protected function redirectTo($request) { if (!$request->expectsJson()) { if ($request->is('admin/*')) { return route('admin.login'); } return route('login'); } }
Now, goto your RedirectIfAuthenticated.php. We need to add check for admin gaurd.
public function handle(Request $request, Closure $next, ...$guards) { $guards = empty($guards) ? [null] : $guards; foreach ($guards as $guard) { if (Auth::guard($guard)->check()) { if ($guard === 'admin') { return redirect()->route('admin.home'); } return redirect(RouteServiceProvider::HOME); } } return $next($request); }

Routing

use Illuminate\Support\Facades\Route; use Laravel\Fortify\Http\Controllers\AuthenticatedSessionController; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::prefix('admin')->name('admin.')->group(function () { Route::view('/login', 'auth.login')->middleware('guest:admin')->name('login'); $limiter = config('fortify.limiters.login'); Route::post('/login', [AuthenticatedSessionController::class, 'store']) ->middleware(array_filter([ 'guest:admin', $limiter ? 'throttle:' . $limiter : null, ])); Route::post('/logout', [AuthenticatedSessionController::class, 'destroy']) ->middleware('auth:admin') ->name('logout'); Route::view('/home', 'admin.home')->middleware('auth:admin')->name('home'); }); Route::get('/', function () { return view('welcome'); }); Route::view('/home', 'user.home')->middleware('auth');

Views

Our welcome.blade.php
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Laravel</title> <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Nunito'; } </style> </head> <body class="antialiased"> @if (Route::has('login')) <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block"> @auth <a href="{{ url('/home') }}" class="text-sm text-gray-700 underline">Home</a> @else <a href="{{ route('login') }}" class="text-sm text-gray-700 underline">Log in</a> @if (Route::has('register')) <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">Register</a> @endif @endauth </div> @endif <div> <h4>Welcome Page</h4> </div> </body> </html>
Our Admin/home.blade.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> HomePage {{ auth()->user()->name }} <form action="{{ route('admin.logout') }}" method="post"> @csrf <button type="submit" class="btn btn-primary">Logout</button> </form> </body> </html>
Our user/home.blade.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> HomePage {{ auth()->user()->name }} <form action="{{ route('admin.logout') }}" method="post"> @csrf <button type="submit" class="btn btn-primary">Logout</button> </form> </body> </html>
Our login page for Admin( auth/login.blade.php) <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Admin Login</title> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback"> {{-- Bootstrap js --}} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Poppins', sans-serif; } </style> </head> <body> <div class="container-fluid row d-flex align-items-center justify-content-center mt-4"> <div class="col-md-6"> <div class="card"> <div class="card-header"> <div class="login-box"> <h3 class="text-center">ADMIN - Elegant Laravel</h3> </div> </div> <div class="card-body mt-3"> <form class="card-body" action="{{ url('admin/login') }}" method="POST"> @csrf @if (Session::has('Invalid')) <div class="alert alert-danger">{{ Session::get('Invalid') }}</div> @endif <div class="input-group mb-3 px-4"> <span class="input-group-text" id="basic-addon1">Email</span> <input type="text" class="form-control" placeholder="abc@xyz.com" name="email"> @error('email') <p class="color-red-500"> </p> @enderror </div> <div class="input-group mb-3 px-4"> <span class="input-group-text" id="basic-addon1">Password</span> <input type="password" class="form-control" placeholder="*******" name="password"> @error('password') <p class="color-red-500"> </p> @enderror </div> <div class="d-grid gap-2 col-4 mx-auto"> <input type="submit" value="Login" class="btn btn-primary btn-lg btn-block"> </div> </form> </div> </div> </div> </div> {{-- Botostrap --}} <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script> {{-- Jquery --}} <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script> </body> </html>
Login page for user (auth/user-login.blade.php)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> {{-- Bootstrap --}} <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <div class="row d-flex align-items-center mt-5"> <div class="col-md-6 card"> <div class="card-header">User Login</div> <form class="card-body" action="{{ url('/login') }}" method="POST"> <div class="form-group"> @csrf <label for="email">Email address</label> <input type="email" name="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email"> @error('email') <span>{{ $message }}</span> @enderror </div> <div class="form-group"> <label for="password">Password</label> <input type="password" name="password" class="form-control" id="password" placeholder="Password"> @error('password') <span>{{ $message }}</span> @enderror </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> {{-- Botostrap --}} <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script> </body> </html>

Output

Admin Laravel Fortify Guard Login Admin homepage after login User laravel fortify gaurd login page
Tags
Multi-Gaurd Laravel Multi Gaurd Fortify Gaurd Fortify Multi Gaurd Laravel Fortify Multi Gaurd Laravel Gaurd Laravel Authentication Multi Authentication Tutorial Multi Role Authentication