Reputation: 51
I'm facing an issue in Laravel related to the middleware after I updated to from Laravel 7 to 9 and Backpack from 4 to 5.
I should get redirected to the login page (example.com/admin/login) when I try to access a route I'm not allowed to without being logged in (like example.com/admin/contacts).
Instead I get an error Call to a member function can() on null
which indicates, that my admin middleware CheckPasswordStatus
is executed before the auth
-middleware.
This is my middleware declaration in Kernel.php
:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'admin' => [
CheckPasswordStatus::class,
],
'api' => [
'throttle:5000,10',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.basic.source' => \App\Http\Middleware\AuthenticateWithBasicAuthForSources::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
I use it in my route declaration in routes/backpack/custom.php as follows:
Route::prefix(config('backpack.base.route_prefix', 'admin'))->middleware([backpack_middleware(), config('backpack.base.web_middleware', 'web')])->group(function () { // custom admin routes
Route::crud('contact', ContactCrudController::class);
Route::crud('conflicts', IncomingImportConflictCrudController::class);
Route::patch('contacts/{id}/advertising-rejection', [ContactCrudController::class, 'advertisingRejection'])->name('contact.advertising_rejection');
Route::post('conflicts/{id}/new', [IncomingImportConflictCrudController::class, 'creatNewContact'])->name('new-contact');
Route::get('conflicts/{id}/compare/{compareId}', [IncomingImportConflictCrudController::class, 'compare']);
});
In my config/backpack/base.php
is the 'middleware_key' => 'admin'
.
The middleware CheckPasswordStatus
simply checks if a user's password has expired and if so, it redirects them to a password reset page:
public function handle($request, Closure $next)
{
if ($user = $request->user()) {
if ($user->passwordIsExpired()) {
return redirect()->route('password.reset.manually');
}
}
return $next($request);
}
When I use something
as the middleware_key
(which isn't defined in my middleware groups), it works as expected, leading to a redirection to the login screen. Shouldn't this throw another error? Maybe there is some default behavior.
When I set the middleware_key back to admin
but comment out the admin
middleware in Kernel.php, everything works fine. (except the CheckPasswordStatus::class
-middleware)
When I use web
as the middleware_key, I get the same error and no redirect.
I tried the two solutions from
Laravel Middleware Error - Call to a member function isBasic() on null
but if I use the authenticated method in 'CheckPasswordStatus::class'
the error says, CheckPasswordStatus middleware not callable
If i use the Auth::check()
as condition in the 'CheckPasswordStatus::class'
I got an empty page instead of a redirect.
Upvotes: 4
Views: 107
Reputation: 40690
You can try explicitly setting the middleware priority, if you are relying on a specific order of middleware execution. Add this in your Kernel.php
:
protected $middlewarePriority = [
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Auth\Middleware\Authorize::class,
CheckPasswordStatus::class
];
This should ensure CheckPasswordStatus
runs after the authenticate and authorise middleware
Upvotes: 1
Reputation: 1071
I think you should be fine by adding your middleware to middleware_class
https://github.com/Laravel-Backpack/CRUD/blob/1b67f8efdbaa48842e0d31995068b44b8fc5c66d/src/config/backpack/base.php#L266
You can get the authenticated user with: backpack_user()
or backpack_auth()->user()
or Auth::guard('admin')->user()
(if admin
is your guard name).
Cheers
Upvotes: 1