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(); });