Files
KanbanGit/tests/Feature/Auth/TwoFactorChallengeTest.php
Twyco 802294d6a2
CI/CD / test_and_check_app (push) Canceled after 0s
CI/CD / Publish immutable image (push) Canceled after 0s
CI/CD / Publish immutable image (pull_request) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 6s
feat(KAN-21): rebuild login verification and two-factor flows
2026-09-11 14:18:23 +02:00

131 lines
4.6 KiB
PHP

<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Inertia\Testing\AssertableInertia as Assert;
use Laravel\Fortify\Contracts\TwoFactorAuthenticationProvider;
use Laravel\Fortify\Features;
use Tests\TestCase;
class TwoFactorChallengeTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->skipUnlessFortifyFeature(Features::twoFactorAuthentication());
}
public function test_two_factor_challenge_redirects_to_login_when_not_authenticated(): void
{
$response = $this->get(route('two-factor.login'));
$response->assertRedirect(route('login'));
}
public function test_two_factor_challenge_can_be_rendered(): void
{
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->create();
$user->forceFill([
'two_factor_secret' => encrypt('test-secret'),
'two_factor_recovery_codes' => encrypt(json_encode(['code1', 'code2'])),
'two_factor_confirmed_at' => now(),
])->save();
$this->post(route('login'), [
'email' => $user->email,
'password' => 'password',
]);
$this->get(route('two-factor.login'))
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('auth/TwoFactorChallenge'),
);
}
public function test_valid_authenticator_code_completes_the_pending_login(): void
{
$user = User::factory()->withTwoFactor()->create();
$this->mock(TwoFactorAuthenticationProvider::class)
->shouldReceive('verify')->once()->with('secret', '012345')->andReturnTrue();
$this->withSession(['url.intended' => route('dashboard')])
->post(route('login.store'), [
'email' => $user->email,
'password' => 'password',
'remember' => true,
])->assertRedirect(route('two-factor.login'));
$this->assertGuest();
$this->post(route('two-factor.login.store'), ['code' => '012345'])
->assertRedirect(route('dashboard'))
->assertSessionMissing('login.id')
->assertSessionMissing('login.remember')
->assertCookie(Auth::guard('web')->getRecallerName());
$this->assertAuthenticatedAs($user);
}
public function test_invalid_authenticator_code_keeps_the_pending_login_for_retry(): void
{
$user = User::factory()->withTwoFactor()->create();
$this->mock(TwoFactorAuthenticationProvider::class)
->shouldReceive('verify')->once()->with('secret', '000000')->andReturnFalse();
$this->withSession(['login.id' => $user->id])
->from(route('two-factor.login'))
->post(route('two-factor.login.store'), ['code' => '000000'])
->assertRedirect(route('two-factor.login'))
->assertSessionHasErrors('code')
->assertSessionHas('login.id', $user->id);
$this->assertGuest();
}
public function test_recovery_code_signs_in_once_and_is_replaced(): void
{
$user = User::factory()->withTwoFactor()->create();
$this->withSession(['login.id' => $user->id, 'login.remember' => true])
->post(route('two-factor.login.store'), ['recovery_code' => 'recovery-code-1'])
->assertRedirect(route('dashboard', absolute: false))
->assertSessionMissing('login.id')
->assertCookie(Auth::guard('web')->getRecallerName());
$this->assertAuthenticatedAs($user);
$this->assertNotContains('recovery-code-1', $user->fresh()->recoveryCodes());
$this->assertCount(1, $user->fresh()->recoveryCodes());
$this->post(route('logout'));
$this->withSession(['login.id' => $user->id])
->post(route('two-factor.login.store'), ['recovery_code' => 'recovery-code-1'])
->assertSessionHasErrors('recovery_code')
->assertSessionHas('login.id', $user->id);
$this->assertGuest();
}
public function test_challenge_submission_cannot_sign_in_without_a_pending_login(): void
{
$this->from(route('two-factor.login'))
->post(route('two-factor.login.store'), ['code' => '012345'])
->assertRedirect(route('two-factor.login'))
->assertSessionHasErrors('code');
$this->assertGuest();
$this->get(route('two-factor.login'))->assertRedirect(route('login'));
}
}