new funcionality
This commit is contained in:
169
tests/Feature/Auth/LoginTest.php
Normal file
169
tests/Feature/Auth/LoginTest.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Auth;
|
||||
|
||||
use App\Models\LoginEvent;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LoginTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_active_approved_user_can_log_in(): void
|
||||
{
|
||||
$user = User::factory()->approved()->create(['password' => Hash::make('password123')]);
|
||||
|
||||
$response = $this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJsonPath('user.email', $user->email);
|
||||
$this->assertAuthenticatedAs($user);
|
||||
$this->assertDatabaseHas('login_events', [
|
||||
'user_id' => $user->id,
|
||||
'type' => 'session',
|
||||
'outcome' => 'success',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_invalid_credentials_are_rejected_and_recorded_as_failure(): void
|
||||
{
|
||||
$user = User::factory()->approved()->create(['password' => Hash::make('password123')]);
|
||||
|
||||
$response = $this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'wrong-password',
|
||||
]);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('email');
|
||||
$this->assertGuest();
|
||||
|
||||
$failure = LoginEvent::where('outcome', 'failure')->first();
|
||||
$this->assertNotNull($failure);
|
||||
$this->assertNull($failure->user_id);
|
||||
}
|
||||
|
||||
public function test_deactivated_account_cannot_log_in(): void
|
||||
{
|
||||
$user = User::factory()->approved()->create([
|
||||
'password' => Hash::make('password123'),
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('email');
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_pending_account_cannot_log_in(): void
|
||||
{
|
||||
$user = User::factory()->create(['password' => Hash::make('password123')]);
|
||||
|
||||
$response = $this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('email');
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_login_with_two_factor_enabled_requires_second_step(): void
|
||||
{
|
||||
$user = $this->createUserWithTwoFactor();
|
||||
|
||||
$response = $this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson(['two_factor' => true]);
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_two_factor_challenge_completes_login_with_valid_totp_code(): void
|
||||
{
|
||||
$user = $this->createUserWithTwoFactor();
|
||||
|
||||
$this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
])->assertOk();
|
||||
|
||||
$code = (new Google2FA())->getCurrentOtp($user->two_factor_secret);
|
||||
|
||||
$response = $this->postJson('/web-api/login/2fa', ['code' => $code]);
|
||||
|
||||
$response->assertOk()->assertJsonPath('user.email', $user->email);
|
||||
$this->assertAuthenticatedAs($user);
|
||||
}
|
||||
|
||||
public function test_two_factor_challenge_rejects_invalid_code(): void
|
||||
{
|
||||
$user = $this->createUserWithTwoFactor();
|
||||
|
||||
$this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
])->assertOk();
|
||||
|
||||
$response = $this->postJson('/web-api/login/2fa', ['code' => '000000']);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('code');
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_two_factor_challenge_accepts_recovery_code_only_once(): void
|
||||
{
|
||||
$user = $this->createUserWithTwoFactor();
|
||||
|
||||
$this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
])->assertOk();
|
||||
|
||||
$this->postJson('/web-api/login/2fa', ['code' => 'AAAA-1111'])->assertOk();
|
||||
$this->assertAuthenticatedAs($user);
|
||||
|
||||
$this->postJson('/web-api/logout')->assertNoContent();
|
||||
|
||||
$this->postJson('/web-api/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'password123',
|
||||
])->assertOk();
|
||||
|
||||
$reuse = $this->postJson('/web-api/login/2fa', ['code' => 'AAAA-1111']);
|
||||
|
||||
$reuse->assertUnprocessable()->assertJsonValidationErrors('code');
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_two_factor_challenge_without_pending_session_fails(): void
|
||||
{
|
||||
$response = $this->postJson('/web-api/login/2fa', ['code' => '123456']);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('code');
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
private function createUserWithTwoFactor(): User
|
||||
{
|
||||
$secret = (new Google2FA())->generateSecretKey();
|
||||
|
||||
return User::factory()->approved()->create([
|
||||
'password' => Hash::make('password123'),
|
||||
'two_factor_secret' => $secret,
|
||||
'two_factor_recovery_codes' => ['AAAA-1111', 'BBBB-2222'],
|
||||
'two_factor_confirmed_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user