├── .github └── workflows │ ├── phpstan.yml │ ├── pint.yml │ └── run-tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── config.php ├── database └── migrations │ └── create_auth_logs_table.php.stub ├── phpstan.neon.dist ├── phpunit.xml.dist ├── src ├── AuthLogServiceProvider.php ├── Listeners │ └── LogAuthAction.php └── Subscribers │ └── AuthEventsSubscriber.php └── tests ├── Custom └── CustomEventsTest.php ├── Events └── EventsTest.php ├── Listeners └── ListenerTest.php └── TestCase.php /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: Run PHPStan 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | phpstan: 11 | name: phpstan 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup PHP 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: '8.2' 20 | coverage: none 21 | 22 | - name: Install composer dependencies 23 | uses: ramsey/composer-install@v2 24 | 25 | - name: Run PHPStan 26 | run: ./vendor/bin/phpstan --error-format=github 27 | -------------------------------------------------------------------------------- /.github/workflows/pint.yml: -------------------------------------------------------------------------------- 1 | name: Run Pint 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | pint: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | with: 17 | ref: ${{ github.head_ref }} 18 | 19 | - name: Run Pint 20 | uses: aglipanci/laravel-pint-action@2.3.0 21 | 22 | - name: Commit changes 23 | uses: stefanzweifel/git-auto-commit-action@v5 24 | with: 25 | commit_message: Fix styling 26 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - php: 8.2 17 | laravel: '11.*' 18 | testbench: '9.*' 19 | larastan: '2.*' 20 | dependency-version: prefer-stable 21 | os: ubuntu-latest 22 | - php: 8.2 23 | laravel: '12.*' 24 | testbench: '10.*' 25 | larastan: '3.1.*' 26 | dependency-version: prefer-stable 27 | os: ubuntu-latest 28 | 29 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 30 | 31 | steps: 32 | - name: Checkout code 33 | uses: actions/checkout@v4 34 | 35 | - name: Cache dependencies 36 | uses: actions/cache@v4 37 | with: 38 | path: ~/.composer/cache/files 39 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 40 | 41 | - name: Setup PHP 42 | uses: shivammathur/setup-php@v2 43 | with: 44 | php-version: ${{ matrix.php }} 45 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick 46 | coverage: none 47 | 48 | - name: Install dependencies 49 | run: | 50 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 51 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction 52 | 53 | - name: Execute tests 54 | run: vendor/bin/phpunit 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .php_cs.cache 5 | .DS_Store 6 | /.idea 7 | /.vscode 8 | .phpunit.result.cache 9 | .php-cs-fixer.cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Label84 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | [MIT License](https://opensource.org/licenses/MIT) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Auth Log 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/label84/laravel-auth-log/v/stable?style=flat-square)](https://packagist.org/packages/label84/laravel-auth-log) 4 | [![MIT Licensed](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/label84/laravel-auth-log.svg?style=flat-square)](https://packagist.org/packages/label84/laravel-auth-log) 6 | ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/label84/laravel-auth-log/run-tests.yml?branch=master&style=flat-square) 7 | 8 | The ``laravel-auth-log`` package will log all the default Laravel authentication events (Login, Attempting, Lockout, etc.) to your database. In the config file you can select the events that you would like to log. It will save the event name, email, user id, ip address and user agent to the database. No other configurations are required. This package could be useful for tracking unwanted activity in your Laravel application. 9 | 10 | - [Laravel Support](#laravel-support) 11 | - [Installation](#installation) 12 | - [Usage](#usage) 13 | - [Tests](#tests) 14 | - [License](#license) 15 | 16 | ## Laravel Support 17 | 18 | | Version | Release | 19 | |---------|---------| 20 | | 12.x | ^1.4 | 21 | | 11.x | ^1.4 | 22 | 23 | ## Installation 24 | 25 | ### 1. Install the package via composer 26 | 27 | ```sh 28 | composer require label84/laravel-auth-log 29 | ``` 30 | 31 | ### 2. Publish the config file and migration 32 | 33 | ```sh 34 | php artisan vendor:publish --provider="Label84\AuthLog\AuthLogServiceProvider" --tag="config" 35 | php artisan vendor:publish --provider="Label84\AuthLog\AuthLogServiceProvider" --tag="migrations" 36 | ``` 37 | 38 | ### 3. Run migration 39 | 40 | ```sh 41 | php artisan migrate 42 | ``` 43 | 44 | ## Usage 45 | 46 | In the config file ``config/authlog.php`` you can (un)comment the events that you'd like to log to your database. 47 | 48 | ```php 49 | // config/authlog.php 50 | 51 | return [ 52 | // ... 53 | 'events' => [ 54 | \Illuminate\Auth\Events\Registered::class, 55 | \Illuminate\Auth\Events\Attempting::class, 56 | // \Illuminate\Auth\Events\Authenticated::class, 57 | \Illuminate\Auth\Events\Login::class, 58 | \Illuminate\Auth\Events\Failed::class, 59 | // \Illuminate\Auth\Events\Validated::class, 60 | \Illuminate\Auth\Events\Verified::class, 61 | // \Illuminate\Auth\Events\Logout::class, 62 | // \Illuminate\Auth\Events\CurrentDeviceLogout::class, 63 | // \Illuminate\Auth\Events\OtherDeviceLogout::class, 64 | \Illuminate\Auth\Events\Lockout::class, 65 | \Illuminate\Auth\Events\PasswordReset::class, 66 | ], 67 | ]; 68 | ``` 69 | 70 | In the same file you can can also change the database connection and table name. 71 | 72 | ### Enable/disable logging 73 | 74 | You can add the ``AUTH_LOG_ENABLED=`` to your ``.env`` file to enable/disable the logging. 75 | 76 | ```php 77 | // .env 78 | 79 | AUTH_LOG_ENABLED=true 80 | ``` 81 | 82 | ### Table format example 83 | 84 | | id | event_name | email | user_id | ip_address | user_agent | context | created_at | 85 | |-|-|-|-|-|-|-|-| 86 | | 1 | Attempting | info@example.org | | 127.0.0.1 | Mozilla/5.0 (Windows NT 10.0... | | 2022-01-10 00:00:00 | 87 | | 2 | Login | | 1 | 127.0.0.1 | Mozilla/5.0 (Windows NT 10.0... | | 2022-01-10 00:00:00 | 88 | 89 | ## Tests 90 | 91 | ```sh 92 | ./vendor/bin/phpstan analyse 93 | ./vendor/bin/phpunit 94 | ``` 95 | 96 | ## License 97 | 98 | [MIT](https://opensource.org/licenses/MIT) 99 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "label84/laravel-auth-log", 3 | "description": "Log user authentication actions in Laravel.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [{ 7 | "name": "Label84", 8 | "email": "info@label84.com" 9 | }], 10 | "require": { 11 | "php": "^8.2", 12 | "illuminate/support": "^11.0|^12.0", 13 | "illuminate/auth": "^11.0|^12.0", 14 | "illuminate/database": "^11.0|^12.0" 15 | }, 16 | "require-dev": { 17 | "orchestra/testbench": "^9.0|^10.0", 18 | "larastan/larastan": "^2.0|^3.1", 19 | "laravel/pint": "^1.21" 20 | }, 21 | "extra": { 22 | "laravel": { 23 | "providers": [ 24 | "Label84\\AuthLog\\AuthLogServiceProvider" 25 | ] 26 | } 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Label84\\AuthLog\\": "src" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Label84\\AuthLog\\Tests\\": "tests" 36 | } 37 | }, 38 | "minimum-stability": "dev", 39 | "prefer-stable": true 40 | } 41 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | env('AUTH_LOG_ENABLED', true), 8 | 9 | /* 10 | * The database table name. 11 | */ 12 | 'table_name' => 'authentication_logs', 13 | 14 | /* 15 | * The database connection for the authentication_logs table. 16 | * 17 | * Leave unchanged to use the Laravel default. 18 | */ 19 | 'database_connection' => env('DB_CONNECTION', 'mysql'), 20 | 21 | /* 22 | * All events that the package will log to the database. 23 | */ 24 | 'events' => [ 25 | \Illuminate\Auth\Events\Registered::class, 26 | \Illuminate\Auth\Events\Attempting::class, 27 | // \Illuminate\Auth\Events\Authenticated::class, 28 | \Illuminate\Auth\Events\Login::class, 29 | \Illuminate\Auth\Events\Failed::class, 30 | // \Illuminate\Auth\Events\Validated::class, 31 | \Illuminate\Auth\Events\Verified::class, 32 | // \Illuminate\Auth\Events\Logout::class, 33 | // \Illuminate\Auth\Events\CurrentDeviceLogout::class, 34 | // \Illuminate\Auth\Events\OtherDeviceLogout::class, 35 | \Illuminate\Auth\Events\Lockout::class, 36 | \Illuminate\Auth\Events\PasswordReset::class, 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /database/migrations/create_auth_logs_table.php.stub: -------------------------------------------------------------------------------- 1 | create(config('authlog.table_name'), function (Blueprint $table) { 12 | $table->id(); 13 | 14 | $table->string('event_name'); 15 | $table->string('email')->nullable(); 16 | 17 | $table->unsignedBigInteger('user_id')->nullable(); 18 | 19 | $table->string('ip_address'); 20 | $table->text('user_agent')->nullable(); 21 | 22 | $table->text('context')->nullable(); 23 | 24 | $table->datetime('created_at')->useCurrent(); 25 | }); 26 | } 27 | 28 | public function down() 29 | { 30 | Schema::connection(config('authlog.database_connection'))->dropIfExists(config('authlog.table_name')); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/larastan/larastan/extension.neon 3 | 4 | parameters: 5 | 6 | paths: 7 | - src 8 | 9 | level: 8 10 | 11 | ignoreErrors: 12 | - identifier: missingType.iterableValue 13 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | 9 | 10 | src/ 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/AuthLogServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../config/config.php', 'authlog'); 14 | 15 | $this->publishes([ 16 | __DIR__.'/../database/migrations/create_auth_logs_table.php.stub' => database_path('migrations/'.date('Y_m_d_His_', time()).'create_auth_logs_table.php'), 17 | ], 'migrations'); 18 | 19 | Event::subscribe(AuthEventsSubscriber::class); 20 | } 21 | 22 | public function boot(): void 23 | { 24 | if ($this->app->runningInConsole()) { 25 | $this->publishes([ 26 | __DIR__.'/../config/config.php' => config_path('authlog.php'), 27 | ], 'config'); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Listeners/LogAuthAction.php: -------------------------------------------------------------------------------- 1 | table(config('authlog.table_name'))->insert([ 23 | 'event_name' => class_basename($event), 24 | 'email' => $this->getEmailParameter($event), 25 | 'user_id' => $this->getUserIdParameter($event), 26 | 'ip_address' => Request::ip(), 27 | 'user_agent' => Request::userAgent(), 28 | 'context' => is_array($context) ? json_encode($context) : null, 29 | 'created_at' => Carbon::now()->timezone(config('app.timezone', 'UTC')), 30 | ]); 31 | } 32 | 33 | /** @param mixed $event */ 34 | private function getEmailParameter($event): ?string 35 | { 36 | if (isset($event->credentials)) { 37 | return $event->credentials['email'] ?? null; 38 | } 39 | 40 | if (isset($event->request) && $event->request->has('email')) { 41 | return $event->request->email; 42 | } 43 | 44 | return null; 45 | } 46 | 47 | /** @param mixed $event */ 48 | private function getUserIdParameter($event): ?string 49 | { 50 | if (isset($event->user)) { 51 | return $event->user->id; 52 | } 53 | 54 | if (Request::user()) { 55 | return Request::user()->id; 56 | } 57 | 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Subscribers/AuthEventsSubscriber.php: -------------------------------------------------------------------------------- 1 | listen(\Illuminate\Auth\Events\Registered::class, [LogAuthAction::class, 'handle']); 13 | $events->listen(\Illuminate\Auth\Events\Attempting::class, [LogAuthAction::class, 'handle']); 14 | $events->listen(\Illuminate\Auth\Events\Authenticated::class, [LogAuthAction::class, 'handle']); 15 | $events->listen(\Illuminate\Auth\Events\Login::class, [LogAuthAction::class, 'handle']); 16 | $events->listen(\Illuminate\Auth\Events\Failed::class, [LogAuthAction::class, 'handle']); 17 | $events->listen(\Illuminate\Auth\Events\Validated::class, [LogAuthAction::class, 'handle']); 18 | $events->listen(\Illuminate\Auth\Events\Verified::class, [LogAuthAction::class, 'handle']); 19 | $events->listen(\Illuminate\Auth\Events\Logout::class, [LogAuthAction::class, 'handle']); 20 | $events->listen(\Illuminate\Auth\Events\CurrentDeviceLogout::class, [LogAuthAction::class, 'handle']); 21 | $events->listen(\Illuminate\Auth\Events\OtherDeviceLogout::class, [LogAuthAction::class, 'handle']); 22 | $events->listen(\Illuminate\Auth\Events\Lockout::class, [LogAuthAction::class, 'handle']); 23 | $events->listen(\Illuminate\Auth\Events\PasswordReset::class, [LogAuthAction::class, 'handle']); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Custom/CustomEventsTest.php: -------------------------------------------------------------------------------- 1 | user, false); 22 | 23 | $context = [ 24 | 'impersonator_id' => 1000, 25 | 'impersonated_id' => 1001, 26 | ]; 27 | 28 | (new LogAuthAction)->handle($event, $context); 29 | 30 | $this->assertCount(1, DB::table('authentication_logs') 31 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Login::class)) 32 | ->whereNull('email') 33 | ->where('user_id', 1000) 34 | ->where('context', json_encode([ 35 | 'impersonator_id' => 1000, 36 | 'impersonated_id' => 1001, 37 | ])) 38 | ->get()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Events/EventsTest.php: -------------------------------------------------------------------------------- 1 | spy(LogAuthAction::class); 22 | 23 | event(new \Illuminate\Auth\Events\Registered($this->user)); 24 | 25 | $logAuthAction->shouldHaveReceived('handle')->once(); 26 | } 27 | 28 | public function test_it_triggers_log_auth_action_on_attempting_event() 29 | { 30 | $logAuthAction = $this->spy(LogAuthAction::class); 31 | 32 | event(new \Illuminate\Auth\Events\Attempting('web', [], false)); 33 | 34 | $logAuthAction->shouldHaveReceived('handle')->once(); 35 | } 36 | 37 | public function test_it_triggers_log_auth_action_on_authenticated_event() 38 | { 39 | $logAuthAction = $this->spy(LogAuthAction::class); 40 | 41 | event(new \Illuminate\Auth\Events\Authenticated('web', [])); 42 | 43 | $logAuthAction->shouldHaveReceived('handle')->once(); 44 | } 45 | 46 | public function test_it_triggers_log_auth_action_on_login_event() 47 | { 48 | $logAuthAction = $this->spy(LogAuthAction::class); 49 | 50 | event(new \Illuminate\Auth\Events\Login('web', $this->user, false)); 51 | 52 | $logAuthAction->shouldHaveReceived('handle')->once(); 53 | } 54 | 55 | public function test_it_triggers_log_auth_action_on_failed_event() 56 | { 57 | $logAuthAction = $this->spy(LogAuthAction::class); 58 | 59 | event(new \Illuminate\Auth\Events\Failed('web', null, [])); 60 | 61 | $logAuthAction->shouldHaveReceived('handle')->once(); 62 | } 63 | 64 | public function test_it_triggers_log_auth_action_on_validated_event() 65 | { 66 | $logAuthAction = $this->spy(LogAuthAction::class); 67 | 68 | event(new \Illuminate\Auth\Events\Validated('web', $this->user)); 69 | 70 | $logAuthAction->shouldHaveReceived('handle')->once(); 71 | } 72 | 73 | public function test_it_triggers_log_auth_action_on_verified_event() 74 | { 75 | $logAuthAction = $this->spy(LogAuthAction::class); 76 | 77 | event(new \Illuminate\Auth\Events\Verified($this->user)); 78 | 79 | $logAuthAction->shouldHaveReceived('handle')->once(); 80 | } 81 | 82 | public function test_it_triggers_log_auth_action_on_logout_event() 83 | { 84 | $logAuthAction = $this->spy(LogAuthAction::class); 85 | 86 | event(new \Illuminate\Auth\Events\Logout('web', $this->user)); 87 | 88 | $logAuthAction->shouldHaveReceived('handle')->once(); 89 | } 90 | 91 | public function test_it_triggers_log_auth_action_on_current_device_logout_event() 92 | { 93 | $logAuthAction = $this->spy(LogAuthAction::class); 94 | 95 | event(new \Illuminate\Auth\Events\CurrentDeviceLogout('web', $this->user)); 96 | 97 | $logAuthAction->shouldHaveReceived('handle')->once(); 98 | } 99 | 100 | public function test_it_triggers_log_auth_action_on_other_device_logout_event() 101 | { 102 | $logAuthAction = $this->spy(LogAuthAction::class); 103 | 104 | event(new \Illuminate\Auth\Events\OtherDeviceLogout('web', $this->user)); 105 | 106 | $logAuthAction->shouldHaveReceived('handle')->once(); 107 | } 108 | 109 | public function test_it_triggers_log_auth_action_on_lockout_event() 110 | { 111 | $logAuthAction = $this->spy(LogAuthAction::class); 112 | 113 | event(new \Illuminate\Auth\Events\Lockout(new Request([]))); 114 | 115 | $logAuthAction->shouldHaveReceived('handle')->once(); 116 | } 117 | 118 | public function test_it_triggers_log_auth_action_on_password_reset_event() 119 | { 120 | $logAuthAction = $this->spy(LogAuthAction::class); 121 | 122 | event(new \Illuminate\Auth\Events\PasswordReset($this->user)); 123 | 124 | $logAuthAction->shouldHaveReceived('handle')->once(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /tests/Listeners/ListenerTest.php: -------------------------------------------------------------------------------- 1 | set('authlog.events', [ 19 | \Illuminate\Auth\Events\Registered::class, 20 | \Illuminate\Auth\Events\Attempting::class, 21 | \Illuminate\Auth\Events\Authenticated::class, 22 | \Illuminate\Auth\Events\Login::class, 23 | \Illuminate\Auth\Events\Failed::class, 24 | \Illuminate\Auth\Events\Validated::class, 25 | \Illuminate\Auth\Events\Verified::class, 26 | \Illuminate\Auth\Events\Logout::class, 27 | \Illuminate\Auth\Events\CurrentDeviceLogout::class, 28 | \Illuminate\Auth\Events\OtherDeviceLogout::class, 29 | \Illuminate\Auth\Events\Lockout::class, 30 | \Illuminate\Auth\Events\PasswordReset::class, 31 | ]); 32 | } 33 | 34 | public function test_it_creates_a_database_record_on_registered_event() 35 | { 36 | event(new \Illuminate\Auth\Events\Registered($this->user)); 37 | 38 | $this->assertCount(1, DB::table('authentication_logs') 39 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Registered::class)) 40 | ->whereNull('email') 41 | ->where('user_id', 1000) 42 | ->get()); 43 | } 44 | 45 | public function test_it_creates_a_database_record_on_attempting_event() 46 | { 47 | event(new \Illuminate\Auth\Events\Attempting('web', ['email' => $this->user->email], false)); 48 | 49 | $this->assertCount(1, DB::table('authentication_logs') 50 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Attempting::class)) 51 | ->where('email', 'info@example.org') 52 | ->whereNull('user_id') 53 | ->get()); 54 | } 55 | 56 | public function test_it_does_not_create_a_database_record_on_authenticated_event() 57 | { 58 | event(new \Illuminate\Auth\Events\Authenticated('web', $this->user)); 59 | 60 | $this->assertCount(0, DB::table('authentication_logs') 61 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Authenticated::class)) 62 | ->where('email', 'info@example.org') 63 | ->where('user_id', $this->user->id) 64 | ->get()); 65 | } 66 | 67 | public function test_it_creates_a_database_record_on_login_event() 68 | { 69 | event(new \Illuminate\Auth\Events\Login('web', $this->user, false)); 70 | 71 | $this->assertCount(1, DB::table('authentication_logs') 72 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Login::class)) 73 | ->whereNull('email') 74 | ->where('user_id', 1000) 75 | ->get()); 76 | } 77 | 78 | public function test_it_creates_a_database_record_on_failed_event() 79 | { 80 | event(new \Illuminate\Auth\Events\Failed('web', null, ['email' => $this->user->email])); 81 | 82 | $this->assertCount(1, DB::table('authentication_logs') 83 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Failed::class)) 84 | ->where('email', 'info@example.org') 85 | ->whereNull('user_id') 86 | ->get()); 87 | } 88 | 89 | public function test_it_creates_a_database_record_on_validated_event() 90 | { 91 | event(new \Illuminate\Auth\Events\Validated('web', $this->user)); 92 | 93 | $this->assertCount(1, DB::table('authentication_logs') 94 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Validated::class)) 95 | ->whereNull('email') 96 | ->where('user_id', 1000) 97 | ->get()); 98 | } 99 | 100 | public function test_it_creates_a_database_record_on_verified_event() 101 | { 102 | event(new \Illuminate\Auth\Events\Verified($this->user)); 103 | 104 | $this->assertCount(1, DB::table('authentication_logs') 105 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Verified::class)) 106 | ->whereNull('email') 107 | ->where('user_id', 1000) 108 | ->get()); 109 | } 110 | 111 | public function test_it_creates_a_database_record_on_logout_event() 112 | { 113 | event(new \Illuminate\Auth\Events\Logout('web', $this->user)); 114 | 115 | $this->assertCount(1, DB::table('authentication_logs') 116 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Logout::class)) 117 | ->whereNull('email') 118 | ->where('user_id', 1000) 119 | ->get()); 120 | } 121 | 122 | public function test_it_creates_a_database_record_on_current_device_logout_event() 123 | { 124 | event(new \Illuminate\Auth\Events\CurrentDeviceLogout('web', $this->user)); 125 | 126 | $this->assertCount(1, DB::table('authentication_logs') 127 | ->where('event_name', class_basename(\Illuminate\Auth\Events\CurrentDeviceLogout::class)) 128 | ->whereNull('email') 129 | ->where('user_id', 1000) 130 | ->get()); 131 | } 132 | 133 | public function test_it_creates_a_database_record_on_other_device_logout_event() 134 | { 135 | event(new \Illuminate\Auth\Events\OtherDeviceLogout('web', $this->user)); 136 | 137 | $this->assertCount(1, DB::table('authentication_logs') 138 | ->where('event_name', class_basename(\Illuminate\Auth\Events\OtherDeviceLogout::class)) 139 | ->whereNull('email') 140 | ->where('user_id', 1000) 141 | ->get()); 142 | } 143 | 144 | public function test_it_creates_a_database_record_on_lockout_event() 145 | { 146 | $request = new Request(['email' => $this->user->email]); 147 | 148 | event(new \Illuminate\Auth\Events\Lockout($request)); 149 | 150 | $this->assertCount(1, DB::table('authentication_logs') 151 | ->where('event_name', class_basename(\Illuminate\Auth\Events\Lockout::class)) 152 | ->where('email', 'info@example.org') 153 | ->get()); 154 | } 155 | 156 | public function test_it_creates_a_database_record_on_password_reset_event() 157 | { 158 | event(new \Illuminate\Auth\Events\PasswordReset($this->user)); 159 | 160 | $this->assertCount(1, DB::table('authentication_logs') 161 | ->where('event_name', class_basename(\Illuminate\Auth\Events\PasswordReset::class)) 162 | ->whereNull('email') 163 | ->where('user_id', 1000) 164 | ->get()); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | setUpDatabase(); 19 | 20 | $this->user = (new UserFactory)->make([ 21 | 'id' => 1000, 22 | 'email' => 'info@example.org', 23 | ]); 24 | } 25 | 26 | protected function getPackageProviders($app): array 27 | { 28 | return [ 29 | AuthLogServiceProvider::class, 30 | ]; 31 | } 32 | 33 | protected function getEnvironmentSetUp($app): void 34 | { 35 | config()->set('authlog.database_connection', 'sqlite'); 36 | config()->set('database.default', 'sqlite'); 37 | config()->set('database.connections.sqlite', [ 38 | 'driver' => 'sqlite', 39 | 'database' => ':memory:', 40 | ]); 41 | } 42 | 43 | protected function setUpDatabase() 44 | { 45 | Schema::create(config('authlog.table_name'), function (Blueprint $table) { 46 | $table->id(); 47 | 48 | $table->string('event_name'); 49 | $table->string('email')->nullable(); 50 | 51 | $table->unsignedBigInteger('user_id')->nullable(); 52 | 53 | $table->string('ip_address'); 54 | $table->text('user_agent')->nullable(); 55 | 56 | $table->text('context')->nullable(); 57 | 58 | $table->datetime('created_at')->useCurrent(); 59 | }); 60 | } 61 | } 62 | --------------------------------------------------------------------------------