Files
Investidor-V2/tests/Feature/Auth/TwoFactorDecryptionFailureTest.php
T
nala 6ddafc95ba
tests / ci (push) Canceled after 0s
oportunidades 4
2026-10-10 07:03:59 +02:00

85 lines
4.2 KiB
PHP

<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Encryption\Encrypter;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Crypt;
use Laravel\Fortify\Actions\EnableTwoFactorAuthentication;
use Laravel\Fortify\Fortify;
use PragmaRX\Google2FA\Google2FA;
use Tests\TestCase;
class TwoFactorDecryptionFailureTest extends TestCase
{
use RefreshDatabase;
private function actor(bool $brokenRecovery = false): User
{
$user = User::factory()->create();
app(EnableTwoFactorAuthentication::class)($user);
$user->forceFill(['two_factor_confirmed_at' => now()])->save();
$foreign = new Encrypter(random_bytes(32), 'AES-256-CBC');
$user->forceFill($brokenRecovery
? ['two_factor_recovery_codes' => $foreign->encrypt('["synthetic-only"]')]
: ['two_factor_secret' => $foreign->encrypt('SYNTHETICQAONLY')])->save();
return $user;
}
public function test_wrong_key_secret_returns_controlled_json_and_cancels_challenge_without_authenticating(): void
{
$user = $this->actor();
$original = $user->getRawOriginal('two_factor_secret');
$this->postJson('/login', ['email' => $user->email, 'password' => 'password'])->assertOk()->assertJson(['two_factor' => true]);
$oldSession = session()->getId();
$this->postJson('/two-factor-challenge', ['code' => '123456'])->assertUnprocessable()->assertJsonValidationErrors('code')
->assertJsonPath('message', 'Não foi possível verificar a autenticação desta conta. Inicie sessão novamente ou contacte o suporte.')
->assertSessionMissing('login.id')->assertSessionMissing('_old_input.code');
$this->assertGuest();
$this->assertNotSame($oldSession, session()->getId());
$this->assertSame($original, $user->fresh()->two_factor_secret);
$this->getJson('/dashboard')->assertUnauthorized();
}
public function test_wrong_key_recovery_codes_are_controlled_and_never_replaced_or_authenticated(): void
{
$user = $this->actor(true);
$original = $user->two_factor_recovery_codes;
$this->postJson('/login', ['email' => $user->email, 'password' => 'password'])->assertOk();
$this->postJson('/two-factor-challenge', ['recovery_code' => 'synthetic-only'])->assertUnprocessable()->assertJsonValidationErrors('recovery_code')
->assertSessionMissing('login.id')->assertSessionMissing('_old_input.recovery_code');
$this->assertGuest();
$this->assertSame($original, $user->fresh()->two_factor_recovery_codes);
}
public function test_browser_failure_redirects_to_login_with_safe_visible_message(): void
{
$user = $this->actor();
$this->post('/login', ['email' => $user->email, 'password' => 'password'])->assertRedirect('/two-factor-challenge');
$this->post('/two-factor-challenge', ['code' => '123456'])->assertRedirect('/login')->assertSessionHasErrors('email')
->assertSessionMissing('login.id')->assertSessionMissing('_old_input.code');
$this->assertGuest();
$this->get('/two-factor-challenge')->assertRedirect('/login');
}
public function test_normal_enrollment_and_valid_mfa_still_authenticate_after_invalid_code_is_rejected(): void
{
$user = User::factory()->create();
app(EnableTwoFactorAuthentication::class)($user);
$user->forceFill(['two_factor_confirmed_at' => now()])->save();
$secret = Fortify::currentEncrypter()->decrypt($user->two_factor_secret);
$this->assertCount(8, $user->recoveryCodes());
$this->assertSame($secret, Crypt::decrypt($user->two_factor_secret));
$this->postJson('/login', ['email' => $user->email, 'password' => 'password'])->assertOk()->assertJson(['two_factor' => true]);
$this->postJson('/two-factor-challenge', ['code' => 'invalid'])->assertUnprocessable();
$this->assertGuest();
$code = (new Google2FA)->getCurrentOtp($secret);
$this->postJson('/two-factor-challenge', ['code' => $code])->assertNoContent();
$this->assertAuthenticatedAs($user);
$this->postJson('/logout')->assertNoContent();
$this->assertGuest();
}
}