Framework: New Authenticate Middleware - multiple guards

Created on 29 Oct 2018  路  12Comments  路  Source: laravel/framework

  • Laravel Version: 5.7.11
  • PHP Version: 7.2.10

Description:

L5.7 includes new middleware to handle and redirect unauthenticated users.
It works well with "web" guard. But using custom guards there is no way (at least I couldn't find it) to get current guard.
Different guard would redirect authenticated users to different route, but guard is always set to "web" instead of current guard.

Steps To Reproduce:

in auth.php - new guard is added: admin

Routes:

Route::group(['middleware' => ['auth:admin']], function () {
    // routes...
});

$this->auth returns AuthManager with protected property $guards: [admin];
but $this->auth->guard() returns "web".

Shouldn't $this->auth->guard() return "admin", instead of "web" SessionGuard, since this route is under middleware auth:admin?

Most helpful comment

I have same issue, right now i am handling multiple guards like this

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Route;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (Route::is('admin.*')) {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

There should be a way to receive the guard name in redirectTo method.

PS

I was able to solve this, see how

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * @var array
     */
    protected $guards = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string[] ...$guards
     * @return mixed
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    public function handle($request, Closure $next, ...$guards)
    {
        $this->guards = $guards;

        return parent::handle($request, $next, ...$guards);
    }

    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (array_first($this->guards) === 'admin') {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

All 12 comments

I have same issue, right now i am handling multiple guards like this

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Route;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (Route::is('admin.*')) {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

There should be a way to receive the guard name in redirectTo method.

PS

I was able to solve this, see how

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * @var array
     */
    protected $guards = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string[] ...$guards
     * @return mixed
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    public function handle($request, Closure $next, ...$guards)
    {
        $this->guards = $guards;

        return parent::handle($request, $next, ...$guards);
    }

    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (array_first($this->guards) === 'admin') {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

I did override on unauthenticated method, inside app/Exceptions/Handler.php:

protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['message' => $exception->getMessage()], 401);
    }

    $guard = array_get($exception->guards(), 0);

    switch ($guard) {
        case 'admin':
            $route = 'admin.login';
            break;
       default:
            $route = 'login';
    }

    return redirect()->guest(route($route));
}

In the class "Authenticate" I just override function "authenticate" and changed function "redirectTo" like this(my custom guard is "admin"):

<?php

namespace App\Http\Middleware;

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

class Authenticate extends Middleware
{

    protected function authenticate($request, array $guards)
    {
        if (empty($guards)) {
            $guards = [null];
        }

        foreach ($guards as $guard) {
            if ($this->auth->guard($guard)->check()) {
                return $this->auth->shouldUse($guard);
            }
        }


        $guard = $guards[0];

        if ($guard == 'admin'){
            $request->path = 'admin.';
        }else{
            $request->path = '';
        }

        throw new AuthenticationException(
            'Unauthenticated.', $guards, $this->redirectTo($request)
        );
    }



    protected function redirectTo($request)
    {
        return route($request->path . 'login');
    }
}

Think we can add this. Just pass along the guards argument to the redirectTo method. Feel free to send in a PR to master.

As said in the linked PRs you could easily solve this by adding a property to your middleware. So gonna close this.

thank you

In the class "Authenticate" I just override function "authenticate" and changed function "redirectTo" like this(my custom guard is "admin"):

<?php

namespace App\Http\Middleware;

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

class Authenticate extends Middleware
{

  protected function authenticate($request, array $guards)
  {
      if (empty($guards)) {
          $guards = [null];
      }

      foreach ($guards as $guard) {
          if ($this->auth->guard($guard)->check()) {
              return $this->auth->shouldUse($guard);
          }
      }


      $guard = $guards[0];

      if ($guard == 'admin'){
          $request->path = 'admin.';
      }else{
          $request->path = '';
      }

      throw new AuthenticationException(
          'Unauthenticated.', $guards, $this->redirectTo($request)
      );
  }



    protected function redirectTo($request)
    {
        return route($request->path . 'login');
    }
}

this code still gives this error
Symfony Component Debug Exception FatalThrowableError (E_ERROR)
Call to undefined method IlluminateAuthTokenGuard::attempt()

handler
just edit
$guard = $exception->guards()[0];

How can we fix this problem in Laravel 7

I have same issue, right now i am handling multiple guards like this

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Route;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (Route::is('admin.*')) {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

There should be a way to receive the guard name in redirectTo method.

PS

I was able to solve this, see how

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * @var array
     */
    protected $guards = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string[] ...$guards
     * @return mixed
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    public function handle($request, Closure $next, ...$guards)
    {
        $this->guards = $guards;

        return parent::handle($request, $next, ...$guards);
    }

    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (array_first($this->guards) === 'admin') {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

Thanks. It works. But In laravel ^7.0, array_first() does not work. It is now Arr::first().

I have same issue, right now i am handling multiple guards like this

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Route;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (Route::is('admin.*')) {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

There should be a way to receive the guard name in redirectTo method.

PS

I was able to solve this, see how

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * @var array
     */
    protected $guards = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string[] ...$guards
     * @return mixed
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    public function handle($request, Closure $next, ...$guards)
    {
        $this->guards = $guards;

        return parent::handle($request, $next, ...$guards);
    }

    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (array_first($this->guards) === 'admin') {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

Thanks. It works. But In laravel ^7.0, array_first() does not work. It is now Arr:first().

It should be Arr::first() not Arr:first().
Just tried Arr:first() and came up with issues.

Also do not forget to add
use IlluminateSupportArr; at the beginning of the file

It worked for me this way.

I have same issue, right now i am handling multiple guards like this

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Route;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (Route::is('admin.*')) {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

There should be a way to receive the guard name in redirectTo method.

PS

I was able to solve this, see how

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
    /**
     * @var array
     */
    protected $guards = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string[] ...$guards
     * @return mixed
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    public function handle($request, Closure $next, ...$guards)
    {
        $this->guards = $guards;

        return parent::handle($request, $next, ...$guards);
    }

    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request $request
     * @return string
     */
    protected function redirectTo($request)
    {
        if (!$request->expectsJson()) {
            if (array_first($this->guards) === 'admin') {
                return route('admin.login');
            }
            return route('login');
        }
    }
}

Thanks. It works. But In laravel ^7.0, array_first() does not work. It is now Arr:first().

It should be Arr::first() not Arr:first().
Just tried Arr:first() and came up with issues.

Also do not forget to add
use IlluminateSupportArr; at the beginning of the file

It worked for me this way.

This worked fine in Laravel 8 :)

$guard = Arr::first( $this->guards );

        switch ( $guard ) {
            case 'supplier':
                return route( 'supplier.login.supplier' );
                break;
        }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  3Comments

Fuzzyma picture Fuzzyma  路  3Comments

progmars picture progmars  路  3Comments

Anahkiasen picture Anahkiasen  路  3Comments

felixsanz picture felixsanz  路  3Comments