laravellaravel-9laravel-breeze

Laravel Breeze Authenticate middleware not redirecting when unauthenticated


I am using Laravel 9, extending Laravel Breeze for authentication. I have routes that are protected by the auth middleware (\App\Http\Middleware\Authenticate.php), in which I've specified that redirectTo() should return route('auth.login'). This was working great until a few days ago when suddenly all stopped! This was around the same time that I introduced Laravel Sanctum for API authentication.

When not logged in, and you try to access a protected route, this 500 error pops up:

Attempt to read property "headers" on bool at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:191

I have tried to debug this and found out that the issue is in the unauthenticated() method in Laravel's main Authenticate.php class. The redirection does not happen properly. When I extend this class in my app's Authenticate.php, I realise that the following works and I get a 404 error:

protected function unauthenticated($request, array $guards)
{
    abort(404);
}

If, however, I do the following, I am back to the original error detailed in the aforementioned link:

protected function unauthenticated($request, array $guards)
{
    return redirect(route('auth.login'));
}

I can't explain why this is the case. If I echo out the redirect, it also kind of works, but this is a hack.

Someone, please help explain what's going on.


Solution

  • After more debugging, I found that the issue was indeed in the Authenticate middleware, and specifically in the unauthenticated() method. This throws the AuthenticationException which in turn is hijacked by a custom Handler I had added as part of the Laravel sanctum that I introduced, and forgotten about, for API authentication.

    The purpose of this handler was to override the default Laravel error message when an API request wasn't authenticated.

    In there, I check if the request was to the /api route and then return the custom error message. If, however, it wasn't, I was returning true. This is the culprit.

    $this->renderable(function (AuthenticationException $e, $request) {
        if ($request->is('api/*')) {
            return response()->json(['error' => 'Not authorised.'], 401);
        }
    
        return true; //Bad
    });
    

    The correct way to do it (when I want Laravel to handle any other requests) is to simply return nothing. The docs say:

    If the closure given to the renderable method does not return a value, Laravel's default exception rendering will be utilized