Files
compras/tests/Feature/Settings/TwoFactorManagementTest.php
2026-11-03 11:51:44 +02:00

76 lines
4.9 KiB
PHP

<?php
use App\Models\User;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Inertia\Testing\AssertableInertia as Assert;
use PragmaRX\Google2FA\Google2FA;
require_once __DIR__.'/../../Support/FortifyMfa.php';
test('mfa management requires recent password confirmation without mutating enrollment on consultation', function () {
foundationMfaRoutes();
$user = User::factory()->create();
$this->actingAs($user)->get('/settings/security')->assertRedirect('/user/confirm-password');
$this->postJson('/user/two-factor-authentication')->assertStatus(423);
expect($user->fresh()->two_factor_secret)->toBeNull();
$this->withSession(['auth.password_confirmed_at' => now()->timestamp])->get('/settings/security')->assertOk()
->assertHeader('Cache-Control', 'max-age=0, no-store, private')->assertInertia(fn (Assert $p) => $p
->where('canManageTwoFactor', true)->where('twoFactorEnabled', false)->where('twoFactorPending', false)->missing('secretKey')->missing('recoveryCodes'));
expect($user->fresh()->two_factor_secret)->toBeNull();
});
test('mfa management completes native enrollment renewal and disable only for the current account', function () {
foundationMfaRoutes();
$user = User::factory()->create();
$other = User::factory()->create();
$this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp]);
$this->postJson('/user/two-factor-authentication', ['user_id' => $other->id])->assertSuccessful();
$secret = $this->getJson('/user/two-factor-secret-key')->assertOk()->assertHeader('Cache-Control', 'max-age=0, no-store, private')->json('secretKey');
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeFalse();
$before = $user->fresh()->getAttributes();
$this->get('/settings/security')->assertInertia(fn (Assert $p) => $p->where('twoFactorPending', true)->where('twoFactorEnabled', false)->missing('secretKey')->missing('recoveryCodes'));
expect($user->fresh()->getAttributes())->toBe($before);
$code = (new Google2FA)->getCurrentOtp($secret);
$this->postJson('/user/confirmed-two-factor-authentication', ['code' => $code])->assertSuccessful();
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeTrue();
$oldCodes = $this->getJson('/user/two-factor-recovery-codes')->assertOk()->assertHeader('Cache-Control', 'max-age=0, no-store, private')->json();
$this->postJson('/user/two-factor-recovery-codes')->assertSuccessful();
$newCodes = $this->getJson('/user/two-factor-recovery-codes')->assertOk()->json();
expect($oldCodes)->toHaveCount(8)->and($newCodes)->toHaveCount(8)->and(array_intersect($oldCodes, $newCodes))->toBe([]);
$this->withSession(['auth.password_confirmed_at' => 0])->deleteJson('/user/two-factor-authentication')->assertStatus(423);
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeTrue();
$this->withSession(['auth.password_confirmed_at' => now()->timestamp])->deleteJson('/user/two-factor-authentication')->assertSuccessful();
expect($user->fresh()->two_factor_secret)->toBeNull()->and($user->fresh()->two_factor_recovery_codes)->toBeNull()->and($user->fresh()->two_factor_confirmed_at)->toBeNull();
expect($other->fresh()->two_factor_secret)->toBeNull();
});
test('mfa management invalid confirmation does not flash authentication codes or activate the account', function () {
foundationMfaRoutes();
$user = User::factory()->create();
$this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp]);
$this->postJson('/user/two-factor-authentication')->assertSuccessful();
$this->from('/settings/security')->post('/user/confirmed-two-factor-authentication', ['code' => 'invalid'])->assertSessionHasErrorsIn('confirmTwoFactorAuthentication', ['code'])
->assertSessionMissing('_old_input.code');
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeFalse();
});
test('mfa management rejects missing csrf and accepts the encrypted xsrf cookie header used by the interface', function () {
foundationMfaRoutes();
$this->instance(PreventRequestForgery::class,
new class(app(), app('encrypter')) extends PreventRequestForgery
{
protected function runningUnitTests(): bool
{
return false;
}
});
$user = User::factory()->create();
$response = $this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp])->get('/settings/security')->assertOk();
$cookie = $response->getCookie('XSRF-TOKEN', false);
expect($cookie)->not->toBeNull();
$this->postJson('/user/two-factor-authentication')->assertStatus(419);
expect($user->fresh()->two_factor_secret)->toBeNull();
$this->withHeader('X-XSRF-TOKEN', $cookie->getValue())->postJson('/user/two-factor-authentication')->assertSuccessful();
expect($user->fresh()->two_factor_secret)->not->toBeNull();
});