Files

137 lines
4.5 KiB
PHP

<?php
use App\Models\User;
use App\Models\UserTrustedDevice;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
test('guest without 2fa session is redirected to login', function () {
$this->get('/2fa')->assertRedirect(route('login', absolute: false));
});
test('2fa view renders when 2fa session is set', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '654321');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->get('/2fa')->assertOk()->assertViewIs('auth.two-factor');
});
test('verify with correct code logs the user in', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '111222');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->post('/2fa', ['code' => '111222'])
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticatedAs($user);
});
test('verify with wrong code keeps user logged out', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '111222');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->post('/2fa', ['code' => '999999'])
->assertSessionHas('error');
$this->assertGuest();
});
test('verify with expired code redirects to login', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '111222');
Session::put('auth.2fa_expires_at', now()->subSecond()->timestamp);
$this->post('/2fa', ['code' => '111222'])
->assertRedirect(route('login', absolute: false))
->assertSessionHas('error');
$this->assertGuest();
});
test('verify rejects code with wrong length', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '111222');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->post('/2fa', ['code' => '12345'])->assertSessionHasErrors('code');
$this->assertGuest();
});
test('trust device option persists a trusted device row', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '888888');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->post('/2fa', ['code' => '888888', 'trust_device' => '1']);
expect(UserTrustedDevice::where('user_id', $user->id)->count())->toBe(1);
});
test('trust device defaults to no row when option is unset', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '777777');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->post('/2fa', ['code' => '777777']);
expect(UserTrustedDevice::where('user_id', $user->id)->count())->toBe(0);
});
test('trusted device cookie skips 2fa view and auto-logs-in', function () {
$user = User::factory()->create();
$deviceId = (string) Str::uuid();
$secret = Str::random(64);
UserTrustedDevice::create([
'user_id' => $user->id,
'device_id' => $deviceId,
'token' => hash('sha256', $secret),
'expires_at' => now()->addDays(30),
]);
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '000000');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->withCookie('2fa_trust_device', $deviceId.'|'.$secret)
->get('/2fa')
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticatedAs($user);
});
test('trusted device cookie with wrong secret does not auto-login', function () {
$user = User::factory()->create();
$deviceId = (string) Str::uuid();
$realSecret = Str::random(64);
UserTrustedDevice::create([
'user_id' => $user->id,
'device_id' => $deviceId,
'token' => hash('sha256', $realSecret),
'expires_at' => now()->addDays(30),
]);
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '000000');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$this->withCookie('2fa_trust_device', $deviceId.'|wrong-secret')
->get('/2fa')
->assertOk();
$this->assertGuest();
});