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.
CodePudding user response:
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