Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel - Unauthenticated redirect issue with multiple authentication

I have a multiple authentication system set up in Laravel 5.7

There is an 'admin' section of the site and a 'learner' section of the site. When you try to access a portion of either site, it redirects you the correct login page if you're not logged in yet.

However, if follow these steps, I come across an issue with the redirects:

  1. I try to access a portion (not the login) of the 'admin' site, which redirects me to the admin login.
  2. Then if I link DIRECTLY to the login section of the 'learner' page and log in, instead of going to the home page it will redirect me back to the admin login.

It properly logs me in, but improperly redirects to the other login page. The issue also happens vice versa, if I get an automatic redirect to the 'learner' then link directly to the admin login page and log in.

I believe I've narrowed the issue down to the unaunthenticated function I've placed in the Exception/Handler.php file, but I can't figure out where to go from there.

protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }
        $guard = array_get($exception->guards(), 0);
        switch ($guard) {
            case 'learner':
                $login = 'learner.login';
                break;
            default:
                $login = 'login';
                break;
        }
        return redirect()->guest(route($login));
    }

Using each separate login page works fine. It's just when you follow the process above that I see issues.

I use separate middleware in each controller like this:

Admin Home Controller

public function __construct()
    {
        $this->middleware('auth');
    }

Admin login controller:

public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

Learner home controller

public function __construct()
    {
        $this->middleware('auth:learner');

    }

Learner login Controller:

public function __construct()
    {
        $this->middleware('guest:learner')->except('logout');
    }

Solution: Clearing out the intended url with Session:forget('url.intended');

protected function unauthenticated($request, AuthenticationException $exception)
    {
        // dd($exception);
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }
        $guard = array_get($exception->guards(), 0);
        switch ($guard) {
            case 'learner':
                $login = 'learner.login';
                break;
            default:
                $login = 'login';
                break;
        }
        Session::forget('url.intented'); 
        return redirect()->route($login);
    }
like image 740
knat Avatar asked Sep 20 '18 04:09

knat


2 Answers

Solution: Clearing out the intended url with Session:forget('url.intended');

protected function unauthenticated($request, AuthenticationException $exception)
    {
        // dd($exception);
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }
        $guard = array_get($exception->guards(), 0);
        switch ($guard) {
            case 'learner':
                $login = 'learner.login';
                break;
            default:
                $login = 'login';
                break;
        }
        Session::forget('url.intented'); 
        return redirect()->route($login);
    }
like image 145
knat Avatar answered Oct 01 '22 17:10

knat


Here's an example of what I use in production for an application that has two user classes which both must be logged into access their separate resources. The User class uses the default Laravel Authentication with no changes, however, my Admin class users use this custom middleware:

<?php

namespace App\Http\Middleware;

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

class AdminMiddleware
{

    public function handle($request, Closure $next)
    {
        if (Auth::guest()) {
            return redirect(route('admin.login'));
        }

        if (!$request->user() instanceof Admin) {
            return redirect(route('restricted'));
        }
        return $next($request);
    }
}

I registered the AdminMiddleware in the app/Http/Kernel.php like so:

protected $routeMiddleware = [
    'admin' => \App\Http\Middleware\AdminMiddleware::class,

    /*** OTHER MIDDLEWARES ARE HERE ***/
];

In the routes/web.php file I have wrapped the routes like so:

Route::group(['prefix' => 'admin', 'as' => 'admin.', 'middleware' => 'auth:admin'], function () {
    /**** ADMIN ROUTES ARE HERE ****/
});

Route::group(['middleware' => 'auth'], function () {
    /**** CLIENT ROUTES ARE HERE ****/
});

And I have a custom AdminLoginController which overwrites a the $redirect path variable as well as the guard(), showLoginForm() and redirectTo() functions and looks like this:

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;

class AdminLoginController extends Controller
{

    use AuthenticatesUsers;

    protected $redirectTo = '/admin/home';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    } 

    public function guard()
    {
        return Auth::guard('admin');
    }

    public function showLoginForm()
    {
        return view('auth.admin.login');
    }

    public function redirectTo()
    {
        return route('admin.home');
    }

}

Finally in the config/auth.php I have the following references to guards, providers and passwords:

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

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

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

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


'providers' => [

    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],

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

],

'passwords' => [
    'admin' => [
        'provider' => 'admins',
        'table' => 'password_resets',
        'expire' => 60,
    ],
    'users' => [
        'provider' => 'users',
        'table' => 'password_resets',
        'expire' => 60,
    ],
],

Of course this means that my Admins and Users are stored on separate tables in the database, but it works really well without any issues so I'm perfectly okay with that.

like image 30
Azeame Avatar answered Oct 01 '22 15:10

Azeame