Filament 3: Login with Name, Username or Email

Tutorial last revisioned on April 01, 2024

By default, the Filament login form contains email and password fields. In this short tutorial, let's see how to log in to the panel using name or email in the same field. Instead of name, you can have username, the logic would be similar.

filament login form with username

We can do this by customizing authentication.

All Filament Panels settings are made in the Service Provider. For this example, we will use the default AdminPanelProvider. Here we have a login() method which accepts an action class as a parameter:

app/Providers/Filament/AdminPanelProvider.php:

class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login(Login::class)
// ...
}
}

Let's create our own Login page and inject it into a login method. I will create this class in the App\Filament\Auth directory. It needs to extend the original Login class from the Filament.

app/Filament/Auth/Login.php:

use Filament\Pages\Auth\Login as BaseAuth;
 
class Login extends BaseAuth
{
// ...
}

You can check the full code of the Filament Login class in the GitHub repository. For our use case, we will need to override three methods:

  • form() - to show the form differently
  • getCredentialsFromFormData() - to process form data correctly
  • throwFailureValidationException() - to show errors properly

Also, we will replace the method getEmailFormComponent() with our own new method called getLoginFormComponent() for using a different field instead of email. We will call that field "Login" which will accept email or username value.

app/Filament/Auth/Login.php:

use Filament\Forms\Form;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Component;
use Filament\Pages\Auth\Login as BaseAuth;
 
class Login extends BaseAuth
{
public function form(Form $form): Form
{
return $form
->schema([
$this->getEmailFormComponent(),
$this->getLoginFormComponent(),
$this->getPasswordFormComponent(),
$this->getRememberFormComponent(),
])
->statePath('data');
}
 
protected function getLoginFormComponent(): Component
{
return TextInput::make('login')
->label('Login')
->required()
->autocomplete()
->autofocus()
->extraInputAttributes(['tabindex' => 1]);
}
}

We have a login form where we can enter name or email for the credentials.

filament login form with username

Now, let's overwrite the getCredentialsFromFormData() method to use either name or email for authenticating.

app/Filament/Auth/Login.php:

class Login extends BaseAuth
{
// ...
 
protected function getCredentialsFromFormData(array $data): array
{
$login_type = filter_var($data['login'], FILTER_VALIDATE_EMAIL ) ? 'email' : 'name';
 
return [
$login_type => $data['login'],
'password' => $data['password'],
];
}
}

In this method, we use the PHP function filter_var to check if the entered value is email. Depending on that, we assign the login_type variable to the DB name email or name.

We have created our custom Login action. Now we just need to inject it into the login method in the AdminPanelProvider.

app/Providers/Filament/AdminPanelProvider.php:

use App\Filament\Auth\Login;
 
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login(Login::class)
// ...
}
}

Finally, the problem is that the validation errors still come for data.email field and not the data.login field, which means the error message won't be visible. We need to fix it, by overwriting another method:

app/Filament/Auth/Login.php:

class Login extends BaseAuth
{
// ...
 
protected function throwFailureValidationException(): never
{
throw ValidationException::withMessages([
'data.login' => __('filament-panels::pages/auth/login.messages.failed'),
]);
}
}

That's it. Users can log in to the Filament panel using name or email in the same field.


If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.

avatar

Nice trick but no error messages are displayed when login failed! I added this to login.php

public function authenticate(): ?LoginResponse
    {
        try {
            return parent::authenticate();
        } catch (ValidationException) {
            throw ValidationException::withMessages([
                'data.login' => __('filament-panels::pages/auth/login.messages.failed'),
            ]);
        }
    }
👍 5
avatar

Is user right password, wrong the username, how to validate?

avatar

Also work adding this instead:

protected function throwFailureValidationException(): never
    {
        throw ValidationException::withMessages([
            'data.login' => __('filament-panels::pages/auth/login.messages.failed'),
        ]);
}
avatar

Thank you, I found these older comments and updated the tutorial accordingly.

avatar

You can add the tab index attribute to the new login field to properly use the tab key to jump between fields:

    protected function getLoginFormComponent(): Component
    {
        return TextInput::make('login')
            ->label('Login')
            ->required()
            ->autocomplete()
            ->autofocus()
            ->extraInputAttributes(['tabindex' => 1]);
    }
avatar

This should be added to the article. Great tips.

avatar

Thank you, I found these older comments and updated the tutorial accordingly.

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1057 lessons, total 42 h 44 min)
  • 79 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials