Basic two factor auth implementation (#1050)

* Basic two factor auth

* Remove unused import

* Add translation
This commit is contained in:
Lance Pioch 2025-03-03 15:22:12 -05:00 committed by GitHub
parent da195fd2fe
commit 36a38ab947
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 0 deletions

View File

@ -3,17 +3,84 @@
namespace App\Filament\Pages\Auth;
use App\Extensions\OAuth\Providers\OAuthProvider;
use App\Models\User;
use Coderflex\FilamentTurnstile\Forms\Components\Turnstile;
use Filament\Facades\Filament;
use Filament\Forms\Components\Actions;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\TextInput;
use Filament\Http\Responses\Auth\Contracts\LoginResponse;
use Filament\Notifications\Notification;
use Filament\Pages\Auth\Login as BaseLogin;
use Filament\Support\Colors\Color;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Sleep;
use Illuminate\Validation\ValidationException;
use PragmaRX\Google2FA\Google2FA;
class Login extends BaseLogin
{
private Google2FA $google2FA;
public bool $verifyTwoFactor = false;
public function boot(Google2FA $google2FA): void
{
$this->google2FA = $google2FA;
}
public function authenticate(): ?LoginResponse
{
$data = $this->form->getState();
Filament::auth()->once($this->getCredentialsFromFormData($data));
/** @var ?User $user */
$user = Filament::auth()->user();
// Make sure that rate limits apply
if (!$user) {
return parent::authenticate();
}
// 2FA disabled
if (!$user->use_totp) {
return parent::authenticate();
}
$token = $data['2fa'] ?? null;
// 2FA not shown yet
if ($token === null) {
$this->verifyTwoFactor = true;
return null;
}
$isValidToken = $this->google2FA->verifyKey(
$user->totp_secret,
$token,
Config::integer('panel.auth.2fa.window'),
);
if (!$isValidToken) {
// Buffer to prevent bruteforce
Sleep::sleep(1);
Notification::make()
->title(trans('auth.failed-two-factor'))
->body(trans('auth.failed'))
->color('danger')
->icon('tabler-auth-2fa')
->danger()
->send();
return null;
}
return parent::authenticate();
}
protected function getForms(): array
{
return [
@ -24,6 +91,7 @@ class Login extends BaseLogin
$this->getPasswordFormComponent(),
$this->getRememberFormComponent(),
$this->getOAuthFormComponent(),
$this->getTwoFactorAuthenticationComponent(),
Turnstile::make('captcha')
->hidden(!config('turnstile.turnstile_enabled'))
->validationMessages([
@ -36,6 +104,15 @@ class Login extends BaseLogin
];
}
private function getTwoFactorAuthenticationComponent(): Component
{
return TextInput::make('2fa')
->label(trans('auth.two-factor-code'))
->hidden(fn () => !$this->verifyTwoFactor)
->required()
->live();
}
protected function throwFailureValidationException(): never
{
$this->dispatch('reset-captcha');

22
lang/en/auth.php Normal file
View File

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'failed-two-factor' => 'Incorrect 2FA Code',
'two-factor-code' => 'Two Factor Code',
'password' => 'The provided password is incorrect.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];