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' => [
AppHttpMiddlewareEncryptCookies::class,
IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
IlluminateSessionMiddlewareStartSession::class,
IlluminateViewMiddlewareShareErrorsFromSession::class,
AppHttpMiddlewareVerifyCsrfToken::class,
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
'admin' => [
CheckPasswordStatus::class,
],
'api' => [
'throttle:5000,10',
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
];
protected $routeMiddleware = [
'auth' => AppHttpMiddlewareAuthenticate::class,
'auth.basic' => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,
'auth.basic.source' => AppHttpMiddlewareAuthenticateWithBasicAuthForSources::class,
'cache.headers' => IlluminateHttpMiddlewareSetCacheHeaders::class,
'can' => IlluminateAuthMiddlewareAuthorize::class,
'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class,
'password.confirm' => IlluminateAuthMiddlewareRequirePassword::class,
'signed' => AppHttpMiddlewareValidateSignature::class,
'throttle' => IlluminateRoutingMiddlewareThrottleRequests::class,
'verified' => IlluminateAuthMiddlewareEnsureEmailIsVerified::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.
2
Answers
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()
orbackpack_auth()->user()
orAuth::guard('admin')->user()
(ifadmin
is your guard name).Cheers
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
:This should ensure
CheckPasswordStatus
runs after the authenticate and authorise middleware