create(); $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'email' => 'logout-log@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'is_active' => true, ]); $this->postJson('/api/auth/login', [ 'email' => 'logout-log@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); $this->postJson('/api/auth/logout')->assertOk(); $row = DB::table('auth_log') ->where('event', 'logout') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and((int) $row->tenant_id)->toBe($tenant->id); }); it('register writes auth_log event=register_success', function () { Tenant::factory()->create(); $response = $this->postJson('/api/auth/register', [ 'email' => 'reg-log-test@example.ru', 'password' => 'fresh-pass-123', 'accept_offer' => true, 'accept_pdn' => true, ]); $response->assertStatus(201); $user = User::where('email', 'reg-log-test@example.ru')->first(); $row = DB::table('auth_log') ->where('event', 'register_success') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and($row->email)->toBe('reg-log-test@example.ru'); }); it('2fa verify success writes auth_log event=2fa_verify_success', function () { $tenant = Tenant::factory()->create(); $google2fa = new Google2FA; $secret = $google2fa->generateSecretKey(); $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'email' => '2fa-log-success@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'is_active' => true, 'totp_enabled' => true, 'totp_secret' => $secret, ]); // Step 1: login to set pending_user_id in session. $this->postJson('/api/auth/login', [ 'email' => '2fa-log-success@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); // Step 2: verify with valid code. $validCode = $google2fa->getCurrentOtp($secret); $this->postJson('/api/auth/2fa/verify', [ 'code' => $validCode, ])->assertOk(); $row = DB::table('auth_log') ->where('event', '2fa_verify_success') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and((int) $row->tenant_id)->toBe($tenant->id); }); it('2fa verify failed writes auth_log event=2fa_verify_failed', function () { $tenant = Tenant::factory()->create(); $google2fa = new Google2FA; $secret = $google2fa->generateSecretKey(); $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'email' => '2fa-log-fail@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'is_active' => true, 'totp_enabled' => true, 'totp_secret' => $secret, ]); // Step 1: login to set pending_user_id in session. $this->postJson('/api/auth/login', [ 'email' => '2fa-log-fail@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); // Step 2: verify with wrong code. $this->postJson('/api/auth/2fa/verify', [ 'code' => '000000', ])->assertStatus(422); $row = DB::table('auth_log') ->where('event', '2fa_verify_failed') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and($row->failure_reason)->toBe('invalid_code'); }); it('2fa recovery used writes auth_log event=2fa_recovery_used', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'email' => '2fa-recovery-log@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'is_active' => true, 'totp_enabled' => true, 'totp_secret' => 'JBSWY3DPEHPK3PXP', ]); DB::table('user_recovery_codes')->insert([ 'user_id' => $user->id, 'code_hash' => Hash::make('abcd1234'), 'used_at' => null, ]); // Login to set pending_user_id. $this->postJson('/api/auth/login', [ 'email' => '2fa-recovery-log@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); $this->postJson('/api/auth/2fa/recovery-use', [ 'code' => 'ABCD-1234', ])->assertOk(); $row = DB::table('auth_log') ->where('event', '2fa_recovery_used') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and((int) $row->tenant_id)->toBe($tenant->id); }); it('2fa recovery failed writes auth_log event=2fa_recovery_failed', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'email' => '2fa-recovery-fail-log@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'is_active' => true, 'totp_enabled' => true, 'totp_secret' => 'JBSWY3DPEHPK3PXP', ]); DB::table('user_recovery_codes')->insert([ 'user_id' => $user->id, 'code_hash' => Hash::make('abcd1234'), 'used_at' => null, ]); // Login to set pending_user_id. $this->postJson('/api/auth/login', [ 'email' => '2fa-recovery-fail-log@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); $this->postJson('/api/auth/2fa/recovery-use', [ 'code' => 'WRONG-9999', ])->assertStatus(422); $row = DB::table('auth_log') ->where('event', '2fa_recovery_failed') ->where('user_id', $user->id) ->latest('id') ->first(); expect($row)->not->toBeNull() ->and($row->failure_reason)->toBe('invalid_or_used'); });