Closed yob-yob closed 3 years ago
This error is not found on Laravel's Jetstream package since that package or should I say Fortify uses a login
route... but if you change the route name from the fortify package the error would still be the same... that is why I suggest a redirect route for login
I don't really know if this issue is a Filament Specific since packages like Fortify could still encounter this issue ONLY IF you change the route name that the package is using...
public function testFilamentLogin()
{
$this->withoutExceptionHandling(['Illuminate\Auth\AuthenticationException']);
$this->expectException(RouteNotFoundException::class);
// World
$user = User::factory()->create();
$this->actingAs($user, 'filament');
// Pre-Assertions
$response = $this->get('/admin');
$response->assertStatus(200);
// Action
$user->update(['password' => Hash::make('test')]);
// Assertions
$response = $this->get('/admin');
}
I've hit this before and not been able to find a solution :( I think it's a Laravel authentication limitation. If you find a solution, feel free to PR it :)
I'm having similar issue in admin panel v2 as described here https://github.com/laravel/framework/issues/27105
Referring to this related issue https://github.com/laravel/framework/issues/37393
I followed one of the suggestion to use Session::flush()
to clear all session data by listening to Illuminate\Auth\Events\Logout
event
@danharrin is this workaround would be possible to be implemented in admin panel v2?
I'm having similar issue in admin panel v2 as described here laravel/framework#27105
Referring to this related issue laravel/framework#37393 I followed one of the suggestion to use
Session::flush()
to clear all session data by listening toIlluminate\Auth\Events\Logout
event@danharrin is this workaround would be possible to be implemented in admin panel v2?
Just tried this solution and it worked.
I'm Using | |
---|---|
laravel/framework | 10.x |
filament/filament | 2.17.21 |
php | 8.1 |
OS | windows |
I had this same issue and got annoyed each time I refreshed the database and got the Exception.
Turns out, it is actually quite the easy fix. I don't know how this would be implemented on a filament/filament
level, but this definitely works for me.
You gotta edit your applications app\Exceptions\Handler
class. The extended ExceptionHandler
class has a protected function called unauthenticated
and that is where the route('login')
is hard coded.
Simply override that function and you're golden.
Here's my class, with some lines removed for brevity:
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
protected function unauthenticated($request, AuthenticationException $exception)
{
return $this->shouldReturnJson($request, $exception)
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest($exception->redirectTo() ?? route('filament.auth.login'));
}
}
As you can see here, I opted to use Filament's filament.auth.login
named route.
Now if you use Filament as an admin panel only, and have a different user portal, you're most likely using custom guards. The $exception
variable in the function contains the used guard in a guards()
method. Using this you could easily change where it redirects to.
I'm Using laravel/framework 10.x filament/filament 2.17.21 php 8.1 OS windows I had this same issue and got annoyed each time I refreshed the database and got the Exception.
Turns out, it is actually quite the easy fix. I don't know how this would be implemented on a
filament/filament
level, but this definitely works for me.You gotta edit your applications
app\Exceptions\Handler
class. The extendedExceptionHandler
class has a protected function calledunauthenticated
and that is where theroute('login')
is hard coded.Simply override that function and you're golden.
Here's my class, with some lines removed for brevity:
<?php namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { public function register(): void { $this->reportable(function (Throwable $e) { // }); } protected function unauthenticated($request, AuthenticationException $exception) { return $this->shouldReturnJson($request, $exception) ? response()->json(['message' => $exception->getMessage()], 401) : redirect()->guest($exception->redirectTo() ?? route('filament.auth.login')); } }
As you can see here, I opted to use Filament's
filament.auth.login
named route.Now if you use Filament as an admin panel only, and have a different user portal, you're most likely using custom guards. The
$exception
variable in the function contains the used guard in aguards()
method. Using this you could easily change where it redirects to.
this solved my issue than you.
I'm Using laravel/framework 10.x filament/filament 2.17.21 php 8.1 OS windows I had this same issue and got annoyed each time I refreshed the database and got the Exception.
Turns out, it is actually quite the easy fix. I don't know how this would be implemented on a
filament/filament
level, but this definitely works for me.You gotta edit your applications
app\Exceptions\Handler
class. The extendedExceptionHandler
class has a protected function calledunauthenticated
and that is where theroute('login')
is hard coded.Simply override that function and you're golden.
Here's my class, with some lines removed for brevity:
<?php namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { public function register(): void { $this->reportable(function (Throwable $e) { // }); } protected function unauthenticated($request, AuthenticationException $exception) { return $this->shouldReturnJson($request, $exception) ? response()->json(['message' => $exception->getMessage()], 401) : redirect()->guest($exception->redirectTo() ?? route('filament.auth.login')); } }
As you can see here, I opted to use Filament's
filament.auth.login
named route.Now if you use Filament as an admin panel only, and have a different user portal, you're most likely using custom guards. The
$exception
variable in the function contains the used guard in aguards()
method. Using this you could easily change where it redirects to.
it works, thank you!
anyone from google:
i always used the pre-hashed string for my user factory passwords
a recent laravel version has changed this logic so now the password hash changes every single time you migrate and seed
therefore, i was suddenly seeing this page in filament every time i re-seeded during dev
the solution was to just change my user factory password back to the hard-coded string from previous laravel versions:
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
I've hit this before and not been able to find a solution :( I think it's a Laravel authentication limitation. If you find a solution, feel free to PR it :)
I fixed this issue by updating the redirectTo function of AuthenticateSession Middleware.
protected function redirectTo(Request $request)
{
// Check if the request URL contains the admin panel path
if (str_contains($request->url(), '/admin')) {
return '/admin/login'; // Redirect to admin login if in the admin panel
}
// Default redirect to the regular login
return '/login';
}
In my case the problem occurs when the user change his own password. I realized that the problem is because I have two panels and also changed the start path of both: one to /app
and another to /admin
.
So when the AuthenticateSession
middleware identifies that the user is not logged in anymore, it redirects to the /login
route that does not exists raising an exception.
I ended using the same approach as @hammadzafar05 but I made it more versatile to identify the panel and ~do a named route redirect~ return the login url.
EDIT: The code was changed after @danharrin question below.
protected function redirectTo(Request $request)
{
return Filament::getCurrentPanel()?->getLoginUrl();
}
Doesnt $panel->getLoginUrl()
work?
Doesnt
$panel->getLoginUrl()
work?
Yes. It worked. I updated my answer above.
I'm aware of the usage of Filament::getLoginUrl()
directly that handles the current panel for me, but it does not have the null safe operator. In reality none of the direct methods on FilamentManager
have the null safe operator. Maybe we need a PR to fix this?
Also, FYI, I did not used it before because I was getting error in another situation. I have two panels as stated above and want to:
admin
(no multi-tenancy panel) in navigation menu of app
panel$panel
->navigationItems([
NavigationItem::make('Admin')
->url(fn () => url(Filament::getPanel('admin')->getPath()))
->icon('heroicon-o-wrench-screwdriver')
->visible(function () {
/** @var \App\Models\User */
$user = auth()->user();
return $user->isAdmin();
})
->sort(1),
])
app
(multi-tenancy panel and also the default panel) in OrganizationResource
action of admin
panelAction::make('access_app')
->label('filament.actions.access_app')
->translateLabel()
->icon('heroicon-m-arrow-top-right-on-square')
->visible(function (Organization $record): bool {
/** @var \App\Models\User */
$user = auth()->user();
return $user->canAccessTenant($record);
})
->url(function (Organization $record): string {
$app = Filament::getPanel('app')->getPath();
$path = "$app/$record->slug";
return url($path);
})
Notice that I used getPath()
on both cases.
This is because the getUrl()
(and also getLoginUrl()
) is giving me:
->url(Filament::getPanel('admin')->getUrl())
default()
method inside a Filament provider's panel()
configuration.
app
panel as default.->url(fn () => Filament::getPanel('admin')->getUrl())
app
does not really have an organizations resource. This resource is the first resource on admin
panel. I guess that it got wrong concatenating the default panel (app
) with the first navigation item from the panel that I actually requested the URL (admin
).->url(fn (Organization $record): string => Filament::getPanel('app')->getUrl($record))
$firstGroup
is null
because $navigation
is an empty array. I guess maybe something while trying to set the tenant, because the existence of two panels or because that the app
panel does not have any resource, it has only the dashboard page and the admin link as navigation items.But probably I misunderstood the concept or usage of something at all. I'm a newbie with Filament: just four days reading docs and two coding.
Do you think I may create a separate issue/discussion and move this into there?
Describe the bug
TL;DR When you login on filament admin then refresh your database with a seeder it throws an
AuthenticationException
error.. this error is thrown byAuthenticateSession.php
I traced the problem and it was because
Authenticate.php
middleware still return a filament user... this is because SessionGuard only gets user data from the session which is still alive since we only freshened out the database and not the session then this passes to the$next
middlewareAuthenticateSession.php
which checks ifpassword_hash_filament
matches the password hash in the database (of course this check would fail since the password hash is now changed due to the refresh)... a logout method is now executed which throws anAuthenticationException
Error... this error should automatically redirect if a value ofRedirectTo
is provided... unfortunatelyAuthenticateSession.php
does not provide this value, hence Laravel chooses a default route tologin
...To reproduce Steps to reproduce the behavior:
php artisan migrate:fresh --seed
php artisan migrate:fresh --seed
AGAINAnother way to reproduce
php artisan migrate:fresh --seed
tinker
\Filament\Models\User::find(1)->update([ 'password' => Hash::make('12334567890') ]);
Expected behavior A redirect to the filament login page
Screenshots If applicable, add screenshots to help explain your problem.
Context
Additional details Is there any way to catch the mismatch of password hash before
AuthenticateSession.php
is executed? Is this even a Filament Issue in the first place? (I could see how Laravel is quite opinionated in here)Quick Fix
login
that would send you back to thefilament.auth.login
routeLogin
route for your App login page... I still suggest you create a redirect route for login and check where the user is coming from and if the user is coming fromFilament
redirect them back tofilament.auth.login
but if they are coming from your App's protected routes you should redirect them back to you own login route some thingapp.auth.login
Example for a redirect