diff --git a/app/Livewire/Auth/TwoFactorSetup.php b/app/Livewire/Auth/TwoFactorSetup.php index 71b3978..f5a9bf8 100644 --- a/app/Livewire/Auth/TwoFactorSetup.php +++ b/app/Livewire/Auth/TwoFactorSetup.php @@ -7,6 +7,7 @@ use Illuminate\Validation\ValidationException; use Livewire\Attributes\Layout; use Livewire\Attributes\Validate; use Livewire\Component; +use PragmaRX\Google2FA\Exceptions\Google2FAException; use PragmaRX\Google2FAQRCode\Google2FA; #[Layout('layouts.auth')] @@ -30,7 +31,13 @@ class TwoFactorSetup extends Component { $this->validate(); - if (! (new Google2FA)->verifyKey($this->secret, preg_replace('/\s+/', '', $this->code))) { + try { + $matches = (new Google2FA)->verifyKey($this->secret, preg_replace('/\s+/', '', $this->code) ?? ''); + } catch (Google2FAException $e) { + $matches = false; + } + + if (! $matches) { throw ValidationException::withMessages(['code' => __('auth.code_mismatch')]); } diff --git a/app/Models/User.php b/app/Models/User.php index e99e87b..f384159 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -11,6 +11,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; +use PragmaRX\Google2FA\Exceptions\Google2FAException; use PragmaRX\Google2FAQRCode\Google2FA; #[Fillable(['name', 'email', 'password', 'must_change_password', 'locale'])] @@ -102,7 +103,7 @@ class User extends Authenticatable try { return (new Google2FA)->verifyKey($this->two_factor_secret, preg_replace('/\s+/', '', $code) ?? ''); - } catch (\Throwable) { + } catch (Google2FAException) { return false; } } diff --git a/tests/Feature/FirstFactorCodesTest.php b/tests/Feature/FirstFactorCodesTest.php index e47d9e3..7a9b81f 100644 --- a/tests/Feature/FirstFactorCodesTest.php +++ b/tests/Feature/FirstFactorCodesTest.php @@ -57,4 +57,17 @@ class FirstFactorCodesTest extends TestCase $this->assertTrue($user->fresh()->hasRecoveryCodes()); } + + public function test_malformed_setup_secret_yields_validation_error_not_500(): void + { + $user = User::factory()->create(); + + Livewire::actingAs($user)->test(TwoFactorSetup::class) + ->set('secret', 'x') // client-tampered, too short for Google2FA + ->set('code', '123456') + ->call('confirm') + ->assertHasErrors('code'); + + $this->assertFalse($user->fresh()->hasTotp()); + } }