diff --git a/app/Http/Controllers/Auth/PasskeyLoginController.php b/app/Http/Controllers/Auth/PasskeyLoginController.php index de2077868..bb3715937 100644 --- a/app/Http/Controllers/Auth/PasskeyLoginController.php +++ b/app/Http/Controllers/Auth/PasskeyLoginController.php @@ -44,6 +44,22 @@ final class PasskeyLoginController extends Controller } } + $passkeyOptionsJson = Session::get('passkey-authentication-options'); + + if (! is_string($passkeyOptionsJson) || $passkeyOptionsJson === '') { + Log::channel('failed_login')->warning( + 'Passkey login submitted without passkey-authentication-options in session from IP address: '.$request->ip() + ); + + session()->flash( + 'authenticatePasskey::message', + 'Your passkey sign-in session expired or was interrupted. Please try signing in with a passkey again.' + ); + session()->flash('authenticatePasskey::reason', 'missing_auth_options'); + + return back(); + } + $findAuthenticatableUsingPasskey = Config::getAction( 'find_passkey', FindPasskeyToAuthenticateAction::class @@ -51,7 +67,7 @@ final class PasskeyLoginController extends Controller $passkey = $findAuthenticatableUsingPasskey->execute( $request->input('start_authentication_response'), - Session::get('passkey-authentication-options'), + $passkeyOptionsJson, ); if (! $passkey || ! $passkey->authenticatable instanceof User) { diff --git a/tests/Feature/Auth/PasskeyAuthenticationTest.php b/tests/Feature/Auth/PasskeyAuthenticationTest.php index a2c30c62a..1233bc7e8 100644 --- a/tests/Feature/Auth/PasskeyAuthenticationTest.php +++ b/tests/Feature/Auth/PasskeyAuthenticationTest.php @@ -128,6 +128,20 @@ class PasskeyAuthenticationTest extends TestCase $this->assertSame('You have not verified your email address!', session('authenticatePasskey::message')); } + public function test_passkey_authentication_without_session_options_redirects_with_message(): void + { + $response = $this + ->from(route('login')) + ->post(route('passkeys.login'), [ + 'start_authentication_response' => json_encode(['id' => 'credential-x'], JSON_THROW_ON_ERROR), + ]); + + $response->assertRedirect(route('login')); + $this->assertGuest(); + $this->assertSame('missing_auth_options', session('authenticatePasskey::reason')); + $this->assertStringContainsString('expired', (string) session('authenticatePasskey::message')); + } + protected function createSchema(): void { Schema::create('settings', function (Blueprint $table): void {