Sitemap

How I Handled Multiple Auth Guards and Redirects in Laravel

3 min readJun 17, 2025

--

When working on a Laravel app with distinct user types, I wanted to:

  • Let both User and Office Model access the same dashboard routes
  • Add a separate admin guard for backend login only
  • Handle login redirects

Sounds simple? It wasn’t. Here’s what I learned along the way.

Phase 1 — Two Models, One Route

Goal: Allow both User and Office to access /dashboard/courses.

My first approach: Create a custom middleware:

public function handle() {
if (Auth::guard('web')->check() || Auth::guard('office')->check()) {
return $next($request);
}

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

Then register it in app/Http/Kernel.php:

protected $routeMiddleware = [
// ...
'auth.dashboard' => \App\Http\Middleware\AuthDashboardUser::class,
];

And apply it to the route:

Route::get('/dashboard/courses', [DashboardController::class, 'courses'])
->middleware('auth.dashboard');

This worked: only User and Office models could access the route. However, if I visited /dashboard/courses directly while unauthenticated, I was redirected to the login page – and after logging in, I landed on /dashboard instead of /dashboard/courses.

Normally, when Laravel redirects you via the Authenticate middleware, it stores the original URL in the session as url.intended. Upon successful login, Laravel uses this value to redirect you to your original destination.

In my custom middleware, that didn’t happen — because url.intended is only set when Laravel throws an AuthenticationException (you could also set it manually in the session, but there might be more edge cases which break functionality.

Learning 1 — Avoid Custom Middleware

Laravel only sets url.intended if it throws an AuthenticationException. One could fix it by adding this to the middleware:

public function handle() {
if (Auth::guard('web')->check() || Auth::guard('office')->check()) {
return $next($request);
}

throw new \Illuminate\Auth\AuthenticationException(
'Unauthenticated.',
['web'],
route('login')
);
}

But a better way is to ditch the custom middleware entirely and use:

Route::middleware('auth:web,office')->group(function () {
Route::get('/dashboard/courses', [DashboardController::class, 'courses']);
});

The notation auth:web,office means, it will apply the auth middlware defined in kernel.php:

protected $routeMiddleware = [
// ...
'auth' => \App\Http\Middleware\Authenticate::class,
];

And the guards web and office are passed as parameter:

In case that the user is not authenticated with any of those guards, an AuthenticationException will be thrown which will then set the url.intended in the session. Usign the Authenticate middlewar will also set the current guard to the auth service, which may have also other important implications.

Scenario 2 — Separate Admin Login

Next, I needed a dedicated admin guard with a separate login route (/admin/login) and its own section.

In config/auth.php:

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

Then:

Route::prefix('admin')->middleware('auth:admin')->group(function () {
// admin-only routes
});

But: Laravel redirected unauthenticated admins to /login, not /admin/login

Learning 2 — Custom Authenticate Middleware is Key

To fix the redirect, I extended Laravel’s Authenticate middleware:

class Authenticate extends \Illuminate\Auth\Middleware\Authenticate
{
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
if ($request->is('admin*')) {
return route('voyager.login'); // or '/admin/login'
}
return route('login');
}
}
}

Then I registered it in Kernel.php (Per default Authenticate class from vendor is taken):

protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
];

Now Laravel redirects correctly depending on the route prefix.

Excerpt from Laravel’s LoginController:

protected function authenticated(Request $request, $user)
{
return redirect()->intended($this->redirectPath());
}

This is what makes url.intended actually work post-login.

--

--

Dr. Adam Nielsen
Dr. Adam Nielsen

Written by Dr. Adam Nielsen

PHD in math. and Laravel / Vue Full-Stack-Developer

No responses yet