A Guide to Override the default Login behavior in FilamentPHP
Introduction
The default login class that comes with Filament is sufficient for most cases since it contains the email
and password
field along with a remember_me
checkbox.
However, there are some cases where you must override the default behavior and implement features accordingly. So, in this article, we'll discuss how we can override the default login class and implement our own features on top of it.
Configuration
Before working on the Custom Login Class, we first need to know how FilamentPhp handles the login flow under the hood. To know that, we'll start by looking into the default Service Provider that comes with FilamentPHP which is the AdminPanelProvider
which lies inside app/Providers/Filament/
namespace.
If we look into that class, there's a login
method chained to the $panel
, which indicates that the login feature should be enabled for this panel.
If we go ahead and look into the implementation of that method, we'll see something like this:
/**
* @param string | Closure | array<class-string, string> | null $action
*/
public function login(string | Closure | array | null $action = Login::class): static
{
$this->loginRouteAction = $action;
return $this;
}
So, by default, if we don't pass any class to this login()
method, the default Login::class
is used.
So, the general idea here is to extend this class, make necessary changes to that class as per our needs, and pass that class to the login()
method.
Defining a Custom Login Class
So, let's start by defining a custom login class by extending from the default login class. We'll define this inside app/Filament/Pages
namespace and name it CustomLogin
.
The CustomLogin::class
looks like this for now:
<?php
namespace App\Filament\Pages;
use Filament\Http\Livewire\Auth\Login;
class CustomLogin extends Login
{
//
}
The default Login class includes everything related to login, like
static
view
property (which we can override if we want to pass our own view)the
authenticate
method (which we can override if we want to update how the user is authenticated)getTitle
andgetHeading
methods, to override the title and headings of the page
and other similar properties and methods which be overridden, based on our needs.
Authenticating with Username instead of Email
As an example, we'll be overriding the default behavior and authenticate with a username instead of the default email.
There are multiple ways to do this, we could override the getForms method and pass a $this->getUsernameFormComponent()
method instead of $this->getEmailFormComponent()
,
and on that getUsernameFormComponent()
method, we could pass a form component for the username field.
But, we'll keep things simple and just override the getEmailFormComponent()
method and pass a form component for the username field.
The getEmailFormComponent()
looks like this when we pass a Form component for the Username field
protected function getEmailFormComponent(): Component
{
return TextInput::make('username')
->label('Username')
->required()
->autofocus()
->extraInputAttributes(['tabindex' => 1])
->autocomplete();
}
We also need to override the getCredentialsFromFormData
method since this method is passed to the authenticate
method, and replace email
key with the username
key.
The getCredentialsFromFormData
looks like this after the changes:
protected function getCredentialsFromFormData(array $data): array
{
return [
'username' => $data['username'],
'password' => $data['password'],
];
}
The final login class after our necessary modifications looks like this:
<?php
namespace App\Filament\Pages;
use Filament\Facades\Filament;
use Filament\Pages\Auth\Login;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\TextInput;
use Illuminate\Contracts\Support\Htmlable;
class CustomLogin extends Login
{
public function mount(): void
{
if (Filament::auth()->check()) {
redirect()->intended(Filament::getUrl());
}
if (app()->environment('local')) {
$this->form->fill([
'username' => 'admin',
'password' => 'password',
]);
}
}
protected function getCredentialsFromFormData(array $data): array
{
return [
'username' => $data['username'],
'password' => $data['password'],
];
}
public function getTitle(): string|Htmlable
{
return __('Admin Login');
}
public function getHeading(): string|Htmlable
{
return __('Admin Login');
}
protected function getEmailFormComponent(): Component
{
return TextInput::make('username')
->label('Username')
->required()
->autofocus()
->extraInputAttributes(['tabindex' => 1])
->autocomplete();
}
}
Along with the methods discussed above, I've also overridden a few more methods, like the getTitle
, getHeading
and the mount
method. The mount method is a bit interesting because it checks whether the user is authenticated, if it is then it will redirect to the intended dashboard.
If it is not logged in, then we'll fill the form with the default Username and Password if the app's environment is local.
FilamentPHP Course
I guess you've already figured out that I love the TALL stack along with FilamentPHP and I have been exploring it a lot lately, and to further improve your skills and help you get started, I have created a Udemy Course based on this stack, where we'll build multiple practical projects and learn a ton of stuff along the way.