DEV Community

Precious Ibeagi
Precious Ibeagi

Posted on

Multi-guard authentication with Laravel 12

In this tutorial i will walk you through implementing multiguard authentication using laravel 12+.

Below are the steps we will have to follow.

  1. Create the model and migration for both admin and users.
  2. Create the required guards.
  3. Define the routes and controllers.
  4. Configure the redirects for the guards.
  5. Logout
  6. Conclusion

Creating the model

We need to have the models we want to create. Make sure that the models extend from the Illuminate\Foundation\Auth\User class

User model

<?php

namespace App\Models;


use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{

    use HasFactory, Notifiable;


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


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


    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Admin model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class Admin extends Authenticatable
{

    use HasFactory, Notifiable;


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


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


    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

Enter fullscreen mode Exit fullscreen mode

Create the required guards

In config/auth.php, define the required guards. The default guard is web while we will define and extra one as admin. Also here we are adding a new admins provider pointing to the App\Models\Admin class

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

'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

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

Enter fullscreen mode Exit fullscreen mode

Define the routes and controllers.

Next we define our routes. So here we have the the basic routes. You'll notice that we have the custom guard that we defined.

<?php

use App\Http\Controllers\AdminDashboardController;
use App\Http\Controllers\AdminLoginController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\LoginController;
use Illuminate\Support\Facades\Route;

Route::middleware('guest')->group(function () {
    Route::get('login', [LoginController::class, 'loginForm'])->name('login');
    Route::post('login', [LoginController::class, 'login'])->name('login-user');
});

Route::middleware('guest:admin')->group(function () {
    Route::get('/admin/login', [AdminLoginController::class, 'loginForm'])->name('admin.login');
    Route::post('/admin/login', [AdminLoginController::class, 'login'])->name('admin.login-admin');
});


Route::middleware('auth')->get('dashboard', DashboardController::class)->name('dashboard');
Route::middleware('auth:admin')->get('/admin/dashboard', AdminDashboardController::class)->name('admin.dashboard');
Enter fullscreen mode Exit fullscreen mode

The LoginController

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function loginForm()
    {
        return view('login');
    }

    public function login()
    {
        $user = User::first();
        Auth::login($user);

        return redirect()->route('dashboard');
    }
}

Enter fullscreen mode Exit fullscreen mode

The AdminLoginController

<?php

namespace App\Http\Controllers;

use App\Models\Admin;
use Illuminate\Support\Facades\Auth;

class AdminLoginController extends Controller
{
    public function loginForm()
    {
        return view('admin.login');
    }

    public function login()
    {
        $admin = Admin::first();
        Auth::guard('admin')->login($admin);
        return redirect()->route('admin.dashboard');
    }
}

Enter fullscreen mode Exit fullscreen mode

Configure the redirects for the guards

You'll notice that when you are not logged in and you try to access the /admin/dashboard as a guest you get redirected to /login instead of /admin/login. So how do we get redirected to the desired urls?

So for that we have to replace the auth and guest aliases in bootstrap/app.php.
So auth aliases the \Illuminate\Auth\Middleware\Authenticate' middleware while the guest aliases the\Illuminate\Auth\Middleware\RedirectIfAuthenticated' middleware.
To configure the redirects, we are going to create our own Authenticate and RedirectIfAuthenticated middleware.

php artisan make:middleware Authenticate
php artisan make:middleware RedirectIfAuthenticated

Enter fullscreen mode Exit fullscreen mode

Now replace the aliases with these new middleware.

<?php

use App\Http\Middleware\Authenticate;
use App\Http\Middleware\RedirectIfAuthenticated;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        commands: __DIR__ . '/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'guest' => RedirectIfAuthenticated::class,
            'auth' => Authenticate::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();


Enter fullscreen mode Exit fullscreen mode

Now for the RedirectIfAuthenticated middleware, in the handle method we just have to set the required destination route if the requesting guard is admin or web.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\RedirectIfAuthenticated as MiddlewareRedirectIfAuthenticated;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class RedirectIfAuthenticated extends MiddlewareRedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next, ...$guards): Response
    {
        $guards = empty($guards) ? [null] : $guards;

        foreach ($guards as $guard) {
            if (Auth::guard($guard)->check()) {
                switch ($guard) {
                    case 'admin':
                        return redirect()->route('admin.dashboard');
                        break;
                    default:
                        return redirect()->route('dashboard');
                }
            }
        }

        return $next($request);
    }
}


Enter fullscreen mode Exit fullscreen mode

Now for the for the Authenticate middleware it's a bit different. We have to modify the $redirectTo parameter of the AuthenticationException that is thrown when the unauthenticated method is called

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate as MiddlewareAuthenticate;

class Authenticate extends MiddlewareAuthenticate
{
    protected function unauthenticated($request, array $guards)
    {

        foreach ($guards as $guard) {
            switch ($guard) {
                case 'admin':
                    throw new AuthenticationException(redirectTo: route('admin.login'));
                    break;
                default:
                    throw new AuthenticationException(redirectTo: route('login'));
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Logout

To logout you just call the guards.

Auth::guard('admin')->logout();
Auth::guard('web')->logout();
Enter fullscreen mode Exit fullscreen mode

Conclusion

And voilà. We now have redirects working as they should. You now have a working multi-guard authentication system. You can add as many models as you choose. You can access the code here.

Top comments (0)