From 953a215aa3238503862a358c7aa46b45abba9f32 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Wed, 4 Mar 2020 12:25:31 -0300 Subject: [PATCH 01/24] Removed unused composer file. --- .github/workflows/composer-coveralls.json | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/workflows/composer-coveralls.json diff --git a/.github/workflows/composer-coveralls.json b/.github/workflows/composer-coveralls.json deleted file mode 100644 index 7df94f2..0000000 --- a/.github/workflows/composer-coveralls.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "require": { - "cedx/coveralls": "*" - }, - "config": { - "vendor-dir": "vendor-coveralls" - } -} From a104ea7f232a5f4c874b1c71deae3375bfb9b5c6 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Wed, 4 Mar 2020 17:24:42 -0300 Subject: [PATCH 02/24] Minor fixes to Github Action workflow. --- .github/workflows/php.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index ed4703f..86c0269 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -33,14 +33,10 @@ jobs: extensions: mbstring, intl coverage: xdebug - - name: Get Composer Cache Directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - name: Cache dependencies uses: actions/cache@v1 with: - path: ${{ steps.composer-cache.outputs.dir }} + path: ~/.composer/cache/files key: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} restore-keys: ${{ runner.os }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer- @@ -49,10 +45,10 @@ jobs: composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-progress --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest - - name: Run test suite + - name: Run Tests run: composer run-script test - - name: Upload coverage results to Coveralls + - name: Upload Coverage to Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_SERVICE_NAME: github From 401b57b93506fda3076fe220c8714588b9c97b21 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Wed, 4 Mar 2020 17:28:04 -0300 Subject: [PATCH 03/24] Added better workflows names. --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 86c0269..56296e7 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -20,7 +20,7 @@ jobs: - laravel: 6.* testbench: 4.* - name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} + name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} steps: - name: Checkout From f2654d280189d30d9c61779015bf3abf399e4f54 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Thu, 5 Mar 2020 20:19:08 -0300 Subject: [PATCH 04/24] Fixed testbench minimum version for Mockery. --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 56296e7..373ece8 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -18,7 +18,7 @@ jobs: - laravel: 7.* testbench: 5.* - laravel: 6.* - testbench: 4.* + testbench: ^4.1 name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} From 00082261b34be01b5ebe3119d682fe96fb494a32 Mon Sep 17 00:00:00 2001 From: Italo Date: Wed, 11 Mar 2020 14:05:39 -0300 Subject: [PATCH 05/24] Laravel version clarification. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84c058e..38c1990 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This package _silently_ enables authentication using 6 digits codes, without Int ## Requirements -* Laravel [6.15](https://blog.laravel.com/laravel-v6-15-0-released) or later. +* Laravel [6.15](https://blog.laravel.com/laravel-v6-15-0-released) or Laravel 7. * PHP 7.2+ ## Table of Contents From 07471495141d8ec5ecc7cf2d7de168471fab42cd Mon Sep 17 00:00:00 2001 From: vagrant <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Wed, 1 Apr 2020 23:30:52 +0000 Subject: [PATCH 06/24] Publish migration instead of running from package Running the migration from the package does not allow customisation of the database table to fit the application. Also, it does not allow the user to determine when the migration is run in relation to migrations. If the migration is published, the user can then modify both the timestamp to dictate when it is run and the column types to suit the needs of the app. E.g. changing ->morphs() to ->uuidMorphs for users using uuid primary key fields. --- ....php => create_two_factor_authentications_table.stub} | 0 src/LaraguardServiceProvider.php | 9 ++++++++- 2 files changed, 8 insertions(+), 1 deletion(-) rename database/migrations/{2020_01_01_000000_create_two_factor_authentications_table.php => create_two_factor_authentications_table.stub} (100%) diff --git a/database/migrations/2020_01_01_000000_create_two_factor_authentications_table.php b/database/migrations/create_two_factor_authentications_table.stub similarity index 100% rename from database/migrations/2020_01_01_000000_create_two_factor_authentications_table.php rename to database/migrations/create_two_factor_authentications_table.stub diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 7fae1b1..5ff6db8 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -34,7 +34,6 @@ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) $this->registerMiddleware($router); $this->loadViewsFrom(__DIR__ . '/../resources/views', 'laraguard'); - $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); $this->loadFactoriesFrom(__DIR__ . '/../database/factories'); if ($this->app->runningInConsole()) { @@ -45,6 +44,14 @@ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) $this->publishes([ __DIR__ . '/../resources/views' => resource_path('views/vendor/laraguard'), ], 'views'); + + if (! class_exists('CreateTwoFactorAuthenticationsTable')) { + $timestamp = date('Y_m_d_His', time()); + + $this->publishes([ + __DIR__.'/../database/migrations/create_two_factor_authentications_table.stub' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), + ], 'migrations'); + } } } From 3ac0e2c7b44cc31d150064f3bc1185a4e356d054 Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 00:47:41 +0100 Subject: [PATCH 07/24] Update the readme with publishable migration changes After making migrations publishable, the readme is now updated to give instructions on how to publish and run the migration --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 38c1990..09f98c8 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,17 @@ It is not invasive, but you can go full manual if you want. ## Usage -First, run the migrations. This will create a table to handle the Two Factor Authentication information for each model you set. +First, publish the migration with: - php artisan migrate:run + php artisan vendor:publish --provider="DarkGhostHunter\Laraguard\LaraguardServiceProvider" --tag="migrations" + +Note: The default migration assumes you are using integers for your user model IDs. If you are using UUIDs, or some other format, adjust the format of the morphs `authenticatable` fields in the published migration before continuing. + +After publishing the migration, you can create the `two_factor_authentications` table by running the migration: + + php artisan migrate + +This will create a table to handle the Two Factor Authentication information for each model you set. Add the `TwoFactorAuthenticatable` _contract_ and the `TwoFactorAuthentication` trait to the User model, or any other model you want to make Two Factor Authentication available. From aea6995e3037abcb28407f7209be4162bf17898c Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 01:23:04 +0100 Subject: [PATCH 08/24] Rename migration stub to include php extension --- ...le.stub => create_two_factor_authentications_table.php.stub} | 0 src/LaraguardServiceProvider.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename database/migrations/{create_two_factor_authentications_table.stub => create_two_factor_authentications_table.php.stub} (100%) diff --git a/database/migrations/create_two_factor_authentications_table.stub b/database/migrations/create_two_factor_authentications_table.php.stub similarity index 100% rename from database/migrations/create_two_factor_authentications_table.stub rename to database/migrations/create_two_factor_authentications_table.php.stub diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 5ff6db8..29617eb 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -49,7 +49,7 @@ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) $timestamp = date('Y_m_d_His', time()); $this->publishes([ - __DIR__.'/../database/migrations/create_two_factor_authentications_table.stub' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), + __DIR__.'/../database/migrations/create_two_factor_authentications_table.php.stub' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), ], 'migrations'); } } From 5aab81fa8ffeca72a157ec9911899160afc4bac8 Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 02:06:28 +0100 Subject: [PATCH 09/24] Remove .stub from migration name and add date prefix Date prefix and php extension required for laravel to load the migration correctly when testing --- ...20_04_02_000000_create_two_factor_authentications_table.php} | 0 src/LaraguardServiceProvider.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename database/migrations/{create_two_factor_authentications_table.php.stub => 2020_04_02_000000_create_two_factor_authentications_table.php} (100%) diff --git a/database/migrations/create_two_factor_authentications_table.php.stub b/database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php similarity index 100% rename from database/migrations/create_two_factor_authentications_table.php.stub rename to database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 29617eb..5ce79ac 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -49,7 +49,7 @@ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) $timestamp = date('Y_m_d_His', time()); $this->publishes([ - __DIR__.'/../database/migrations/create_two_factor_authentications_table.php.stub' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), + __DIR__.'/../database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), ], 'migrations'); } } From b07039a3415f7d8168025aaf61a2e13b3b503258 Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 02:13:59 +0100 Subject: [PATCH 10/24] Create a trait to run publishable migrations Migrations which are publishable are not caught with the `loadLaravelMigrations` method. This trait specifies the folder to use for publishable migrations and adds them to the list of migrations. --- tests/RunsPublishableMigrations.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/RunsPublishableMigrations.php diff --git a/tests/RunsPublishableMigrations.php b/tests/RunsPublishableMigrations.php new file mode 100644 index 0000000..f9d5178 --- /dev/null +++ b/tests/RunsPublishableMigrations.php @@ -0,0 +1,20 @@ +loadMigrationsFrom([ + '--realpath' => true, + '--path' => [ + realpath(__DIR__ . '/../database/migrations') + ] + ]); + } +} From 6ce18f2179d7ae083247d2365b250520b70c4f95 Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 02:15:27 +0100 Subject: [PATCH 11/24] Update tests to work with publishable migrations Uses new RunsPublishableMigrations trait --- tests/Eloquent/TwoFactorAuthenticationTest.php | 3 +++ tests/Events/EventsTest.php | 3 +++ tests/Http/Middleware/EnsureTwoFactorEnabledTest.php | 3 +++ tests/Listeners/ForcesTwoFactorAuthTest.php | 3 +++ tests/Listeners/ListenerNotRegisteredTest.php | 3 +++ tests/TwoFactorAuthenticationTest.php | 2 ++ 6 files changed, 17 insertions(+) diff --git a/tests/Eloquent/TwoFactorAuthenticationTest.php b/tests/Eloquent/TwoFactorAuthenticationTest.php index 5a756ed..37c22ce 100644 --- a/tests/Eloquent/TwoFactorAuthenticationTest.php +++ b/tests/Eloquent/TwoFactorAuthenticationTest.php @@ -3,6 +3,7 @@ namespace Tests\Eloquent; use Carbon\Carbon; +use Tests\RunsPublishableMigrations; use Tests\RegistersPackage; use Orchestra\Testbench\TestCase; use ParagonIE\ConstantTime\Base32; @@ -16,12 +17,14 @@ class TwoFactorAuthenticationTest extends TestCase { use RegistersPackage; use DatabaseMigrations; + use RunsPublishableMigrations; protected $tfa; protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); parent::setUp(); } diff --git a/tests/Events/EventsTest.php b/tests/Events/EventsTest.php index 6096761..ad61f93 100644 --- a/tests/Events/EventsTest.php +++ b/tests/Events/EventsTest.php @@ -2,6 +2,7 @@ namespace Tests\Events; +use Tests\RunsPublishableMigrations; use Tests\RegistersPackage; use Illuminate\Support\Str; use Tests\CreatesTwoFactorUser; @@ -18,11 +19,13 @@ class EventsTest extends TestCase { use RegistersPackage; use DatabaseMigrations; + use RunsPublishableMigrations; use CreatesTwoFactorUser; protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); $this->afterApplicationCreated([$this, 'createTwoFactorUser']); parent::setUp(); diff --git a/tests/Http/Middleware/EnsureTwoFactorEnabledTest.php b/tests/Http/Middleware/EnsureTwoFactorEnabledTest.php index e40e171..fd13d20 100644 --- a/tests/Http/Middleware/EnsureTwoFactorEnabledTest.php +++ b/tests/Http/Middleware/EnsureTwoFactorEnabledTest.php @@ -2,6 +2,7 @@ namespace Tests\Http\Middleware; +use Tests\RunsPublishableMigrations; use Tests\Stubs\UserStub; use Tests\RegistersPackage; use Tests\CreatesTwoFactorUser; @@ -12,11 +13,13 @@ class EnsureTwoFactorEnabledTest extends TestCase { use RegistersPackage; use DatabaseMigrations; + use RunsPublishableMigrations; use CreatesTwoFactorUser; protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); $this->afterApplicationCreated([$this, 'createTwoFactorUser']); $this->afterApplicationCreated(function () { diff --git a/tests/Listeners/ForcesTwoFactorAuthTest.php b/tests/Listeners/ForcesTwoFactorAuthTest.php index 5e1518e..4353b36 100644 --- a/tests/Listeners/ForcesTwoFactorAuthTest.php +++ b/tests/Listeners/ForcesTwoFactorAuthTest.php @@ -2,6 +2,7 @@ namespace Tests\Listeners; +use Tests\RunsPublishableMigrations; use Tests\Stubs\UserStub; use Tests\RegistersPackage; use Illuminate\Support\Str; @@ -18,6 +19,7 @@ class ForcesTwoFactorAuthTest extends TestCase { use RegistersPackage; use DatabaseMigrations; + use RunsPublishableMigrations; use CreatesTwoFactorUser; use RegistersLoginRoute; use WithFaker; @@ -25,6 +27,7 @@ class ForcesTwoFactorAuthTest extends TestCase protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); $this->afterApplicationCreated([$this, 'createTwoFactorUser']); $this->afterApplicationCreated([$this, 'registerLoginRoute']); parent::setUp(); diff --git a/tests/Listeners/ListenerNotRegisteredTest.php b/tests/Listeners/ListenerNotRegisteredTest.php index 8e5ec72..ef8ac85 100644 --- a/tests/Listeners/ListenerNotRegisteredTest.php +++ b/tests/Listeners/ListenerNotRegisteredTest.php @@ -2,6 +2,7 @@ namespace Tests\Listeners; +use Tests\RunsPublishableMigrations; use Tests\RegistersPackage; use Tests\RegistersLoginRoute; use Tests\CreatesTwoFactorUser; @@ -12,6 +13,7 @@ class ListenerNotRegisteredTest extends TestCase { use DatabaseMigrations; + use RunsPublishableMigrations; use RegistersPackage; use CreatesTwoFactorUser; use RegistersLoginRoute; @@ -19,6 +21,7 @@ class ListenerNotRegisteredTest extends TestCase protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); $this->afterApplicationCreated([$this, 'registerLoginRoute']); $this->afterApplicationCreated([$this, 'createTwoFactorUser']); parent::setUp(); diff --git a/tests/TwoFactorAuthenticationTest.php b/tests/TwoFactorAuthenticationTest.php index c246f16..9cc67ad 100644 --- a/tests/TwoFactorAuthenticationTest.php +++ b/tests/TwoFactorAuthenticationTest.php @@ -20,6 +20,7 @@ class TwoFactorAuthenticationTest extends TestCase { use DatabaseMigrations; + use RunsPublishableMigrations; use RegistersPackage; use CreatesTwoFactorUser; use RegistersLoginRoute; @@ -28,6 +29,7 @@ class TwoFactorAuthenticationTest extends TestCase protected function setUp() : void { $this->afterApplicationCreated([$this, 'loadLaravelMigrations']); + $this->afterApplicationCreated([$this, 'runPublishableMigration']); $this->afterApplicationCreated([$this, 'registerLoginRoute']); $this->afterApplicationCreated([$this, 'createTwoFactorUser']); parent::setUp(); From c6f0ee0c82b021043ae8efe395d2c3949bf5b834 Mon Sep 17 00:00:00 2001 From: Jerome Fitzpatrick <20955705+jeromefitzpatrick@users.noreply.github.com> Date: Thu, 2 Apr 2020 02:25:12 +0100 Subject: [PATCH 12/24] Fix trait loading unnecessary trait This was a mistake - now fixed --- tests/RunsPublishableMigrations.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/RunsPublishableMigrations.php b/tests/RunsPublishableMigrations.php index f9d5178..09896f8 100644 --- a/tests/RunsPublishableMigrations.php +++ b/tests/RunsPublishableMigrations.php @@ -2,12 +2,8 @@ namespace Tests; -use Illuminate\Foundation\Testing\DatabaseMigrations; - trait RunsPublishableMigrations { - use DatabaseMigrations; - protected function runPublishableMigration() { $this->loadMigrationsFrom([ From a6daa79f9dfecfaf23d21ad2cd3e561215fd5377 Mon Sep 17 00:00:00 2001 From: Ben Bjurstrom Date: Sat, 4 Apr 2020 12:38:20 -0700 Subject: [PATCH 13/24] Configurable model and listener --- config/laraguard.php | 22 ++++++++++++++++++---- src/LaraguardServiceProvider.php | 12 ++++-------- src/TwoFactorAuthentication.php | 6 +++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/config/laraguard.php b/config/laraguard.php index 8c25c72..ecee51d 100644 --- a/config/laraguard.php +++ b/config/laraguard.php @@ -7,13 +7,27 @@ | Listener hook |-------------------------------------------------------------------------- | - | If the Listener is enabled, Laraguard will automatically hook into the - | "Attempting" event and magically ask for Two Factor Authentication if - | is necessary. Disable this to use your own 2FA authentication logic. + | If the Listener class is present, Laraguard will automatically hook into + | the "Attempting" event and magically ask for Two Factor Authentication if + | is necessary. Set this value to false to use your own 2FA authentication + | logic. | */ - 'listener' => true, + 'listener' => DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, + + /* + |-------------------------------------------------------------------------- + | TwoFactorAuthentication Model + |-------------------------------------------------------------------------- + | + | When using the "TwoFactorAuthentication" trait from this package, we need + | to know which Eloquent model should be used to retrieve your two factor + | authentication records. + | + */ + + 'model' => DarkGhostHunter\Laraguard\Eloquent\TwoFactorAuthentication::class, /* |-------------------------------------------------------------------------- diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 7fae1b1..40af7b6 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -70,14 +70,10 @@ protected function registerListener(Repository $config, Dispatcher $dispatcher) return; } - $this->app->singleton(Listeners\EnforceTwoFactorAuth::class, function ($app) { - return new Listeners\EnforceTwoFactorAuth($app['config'], $app['request']); + $this->app->singleton($config['laraguard.listener'], function ($app) use ($config) { + return new $config['laraguard.listener']($app['config'], $app['request']); }); - $dispatcher->listen(Attempting::class, - 'DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth@saveCredentials' - ); - $dispatcher->listen(Validated::class, - 'DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth@checkTwoFactor' - ); + $dispatcher->listen(Attempting::class, $config['laraguard.listener'] . '@saveCredentials'); + $dispatcher->listen(Validated::class, $config['laraguard.listener'] . '@checkTwoFactor'); } } diff --git a/src/TwoFactorAuthentication.php b/src/TwoFactorAuthentication.php index badfbe5..37adf31 100644 --- a/src/TwoFactorAuthentication.php +++ b/src/TwoFactorAuthentication.php @@ -28,7 +28,7 @@ public function initializeTwoFactorAuthentication() */ public function twoFactorAuth() { - return $this->morphOne(Eloquent\TwoFactorAuthentication::class, 'authenticatable') + return $this->morphOne(config('laraguard.model'), 'authenticatable') ->withDefault(config('laraguard.totp')); } @@ -184,7 +184,7 @@ public function generateRecoveryCodes() : Collection { [$enabled, $amount, $length] = array_values(config('laraguard.recovery')); - $this->twoFactorAuth->recovery_codes = Eloquent\TwoFactorAuthentication::generateRecoveryCodes($amount, $length); + $this->twoFactorAuth->recovery_codes = config('laraguard.model')::generateRecoveryCodes($amount, $length); $this->twoFactorAuth->recovery_codes_generated_at = now(); $this->twoFactorAuth->save(); @@ -248,7 +248,7 @@ public function addSafeDevice(Request $request) : string */ protected function generateTwoFactorRemember() { - return Eloquent\TwoFactorAuthentication::generateDefaultTwoFactorRemember(); + return config('laraguard.model')::generateDefaultTwoFactorRemember(); } /** From 756de0b88effaeb3bf347eccef7a6906f25eecfb Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 13:43:21 -0400 Subject: [PATCH 14/24] Added minimum stability, prefer stable, and modified version constraints. --- composer.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f50f18b..3d48531 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,8 @@ "2fa" ], "homepage": "https://github.com/darkghosthunter/laraguard", + "minimum-stability": "dev", + "prefer-stable": true, "license": "MIT", "type": "library", "authors": [ @@ -21,8 +23,8 @@ "require": { "php": "^7.2.15", "ext-json": "*", - "bacon/bacon-qr-code": "2.*", - "paragonie/constant_time_encoding": "2.*", + "bacon/bacon-qr-code": "^2.0", + "paragonie/constant_time_encoding": "^2.0", "illuminate/support": "^6.15||^7.0", "illuminate/auth": "^6.15||^7.0" }, From 2043a8917869d72c9602c2d8450c449825661ec3 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 13:59:02 -0400 Subject: [PATCH 15/24] Fixed Orchestra Canvas requirement to ^5.0. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3d48531..bdcb611 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ }, "require-dev": { "orchestra/testbench": "^4.0||^5.0", - "orchestra/canvas": "^4.0||5.0", + "orchestra/canvas": "^4.0||^5.0", "phpunit/phpunit": "^8.0" }, "autoload": { From 7e225d246ba9378b53a7d221331d01524ec2ffcd Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 18:23:07 -0400 Subject: [PATCH 16/24] Added backslash to classes. --- README.md | 2 +- config/laraguard.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 09f98c8..f69d953 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ You will receive the authentication view in `resources/views/vendor/laraguard/au ```php return [ - 'listener' => true, + 'listener' => \DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, 'input' => '2fa_code', 'cache' => [ 'store' => null, diff --git a/config/laraguard.php b/config/laraguard.php index ecee51d..4acf157 100644 --- a/config/laraguard.php +++ b/config/laraguard.php @@ -14,7 +14,7 @@ | */ - 'listener' => DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, + 'listener' => \DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, /* |-------------------------------------------------------------------------- @@ -27,7 +27,7 @@ | */ - 'model' => DarkGhostHunter\Laraguard\Eloquent\TwoFactorAuthentication::class, + 'model' => \DarkGhostHunter\Laraguard\Eloquent\TwoFactorAuthentication::class, /* |-------------------------------------------------------------------------- From 2396ad581b8388318ef451d35be915ce042cc41b Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 18:34:27 -0400 Subject: [PATCH 17/24] Indented the config file text. --- config/laraguard.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/laraguard.php b/config/laraguard.php index 4acf157..4cc3b48 100644 --- a/config/laraguard.php +++ b/config/laraguard.php @@ -7,10 +7,9 @@ | Listener hook |-------------------------------------------------------------------------- | - | If the Listener class is present, Laraguard will automatically hook into - | the "Attempting" event and magically ask for Two Factor Authentication if - | is necessary. Set this value to false to use your own 2FA authentication - | logic. + | If a Listener class is present, Laraguard will hook into the Attempting + | and Retrieved events and check if it needs Two Factor Authentication. + | Set this to false or null to use your own 2FA logic without events. | */ @@ -23,7 +22,7 @@ | | When using the "TwoFactorAuthentication" trait from this package, we need | to know which Eloquent model should be used to retrieve your two factor - | authentication records. + | authentication records. You can use your own for more advanced logic. | */ From 5ce25b583cb53c70403e6cf7c1cec66770d7af50 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 18:43:42 -0400 Subject: [PATCH 18/24] Fixed the indentation on the config file. --- config/laraguard.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/laraguard.php b/config/laraguard.php index 4cc3b48..b860355 100644 --- a/config/laraguard.php +++ b/config/laraguard.php @@ -96,9 +96,9 @@ | Secret Length |-------------------------------------------------------------------------- | - | Using a shared secret with a length of 160-bit (as recommended per RFC - | 4226) is recommended, but you may want to tighten or loose the secret - | length. The RFC 4226 standard allows down to 128-bit shared secrets. + | The package uses a shared secret length of 160-bit, as recommended by the + | RFC 4226. This makes it compatible with most 2FA apps. You can change it + | freely but consider the standard allows shared secrets down to 128-bit. | */ From 4752c800d03758b621d2be7d19aa411fb7349816 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 19:06:04 -0400 Subject: [PATCH 19/24] Added contract to Listener. --- src/Contracts/TwoFactorAuthListener.php | 25 +++++++++++++++++++++++++ src/Listeners/EnforceTwoFactorAuth.php | 5 +++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/Contracts/TwoFactorAuthListener.php diff --git a/src/Contracts/TwoFactorAuthListener.php b/src/Contracts/TwoFactorAuthListener.php new file mode 100644 index 0000000..61fc0f5 --- /dev/null +++ b/src/Contracts/TwoFactorAuthListener.php @@ -0,0 +1,25 @@ + Date: Tue, 7 Apr 2020 19:17:05 -0400 Subject: [PATCH 20/24] Modified the singleton to use the contract. --- src/LaraguardServiceProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 55a99f1..cc54b03 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -8,6 +8,7 @@ use Illuminate\Support\ServiceProvider; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Config\Repository; +use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthListener; class LaraguardServiceProvider extends ServiceProvider { @@ -77,9 +78,10 @@ protected function registerListener(Repository $config, Dispatcher $dispatcher) return; } - $this->app->singleton($config['laraguard.listener'], function ($app) use ($config) { + $this->app->singleton(TwoFactorAuthListener::class, function ($app) use ($config) { return new $config['laraguard.listener']($app['config'], $app['request']); }); + $dispatcher->listen(Attempting::class, $config['laraguard.listener'] . '@saveCredentials'); $dispatcher->listen(Validated::class, $config['laraguard.listener'] . '@checkTwoFactor'); } From c74799898ba2ff8d153883e9aa00fcf487e51cc0 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 22:01:52 -0400 Subject: [PATCH 21/24] Fixed typo on the event name. --- config/laraguard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/laraguard.php b/config/laraguard.php index b860355..a5ab2f5 100644 --- a/config/laraguard.php +++ b/config/laraguard.php @@ -8,7 +8,7 @@ |-------------------------------------------------------------------------- | | If a Listener class is present, Laraguard will hook into the Attempting - | and Retrieved events and check if it needs Two Factor Authentication. + | and Validated events and check if it needs Two Factor Authentication. | Set this to false or null to use your own 2FA logic without events. | */ From 8d98286a0a3a9bc2929749955f60412d13710d37 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 22:02:40 -0400 Subject: [PATCH 22/24] Fixed test to use the contract. --- README.md | 27 ++++++++-- phpunit.xml.dist | 1 - ...AuthListener.php => TwoFactorListener.php} | 2 +- src/LaraguardServiceProvider.php | 52 ++++++++++++------- src/Listeners/EnforceTwoFactorAuth.php | 4 +- tests/Listeners/ForcesTwoFactorAuthTest.php | 6 +-- 6 files changed, 61 insertions(+), 31 deletions(-) rename src/Contracts/{TwoFactorAuthListener.php => TwoFactorListener.php} (94%) diff --git a/README.md b/README.md index f69d953..6dc7e99 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ That's it. This packages adds a **Contract** to detect in a **per-user basis** if it should use Two Factor Authentication. It includes a custom **view** and a **listener** to handle the Two Factor authentication itself during login attempts. -It is not invasive, but you can go full manual if you want. +This package was made to be the less invasive possible, but you can go full manual if you want. ## Usage @@ -60,7 +60,7 @@ First, publish the migration with: php artisan vendor:publish --provider="DarkGhostHunter\Laraguard\LaraguardServiceProvider" --tag="migrations" -Note: The default migration assumes you are using integers for your user model IDs. If you are using UUIDs, or some other format, adjust the format of the morphs `authenticatable` fields in the published migration before continuing. +> The default migration assumes you are using integers for your user model IDs. If you are using UUIDs, or some other format, adjust the format of the morphs `authenticatable` fields in the published migration before continuing. After publishing the migration, you can create the `two_factor_authentications` table by running the migration: @@ -252,6 +252,7 @@ You will receive the authentication view in `resources/views/vendor/laraguard/au ```php return [ 'listener' => \DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, + 'model' => \DarkGhostHunter\Laraguard\Eloquent\TwoFactorAuthentication::class, 'input' => '2fa_code', 'cache' => [ 'store' => null, @@ -281,13 +282,29 @@ return [ ```php return [ - 'listener' => true, + 'listener' => \DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth::class, ]; ``` -This package works by hooking up the `ForcesTwoFactorAuth` listener to the `Attempting` and `Validated` events as a fallback. +This package works out-of-the-box by hooking up the `ForcesTwoFactorAuth` listener to the `Attempting` and `Validated` events, which is in charge of checking if the user login needs a 2FA code or not. + +This may work wonders, but if you want tighter control on how and when prompt for Two Factor Authentication, you can use another listener, or disable it. For example, to create your own 2FA Guard or greatly modify the Login Controller. + +> If you change it for your own Listener, ensure it implements the `TwoFactorAuthListener` contract. + +### Model + +```php +return [ + 'model' => \DarkGhostHunter\Laraguard\Eloquent\TwoFactorAuthentication::class, +]; +``` + +This is the model where the the Two Factor Authentication data is saved and associated to the User model. + +You can change this model for your own to create, validate and show the shared secret. If you plan to change where and how this is stored, you can just change the model itself or extend it. -This may work wonders out-of-the-box, but if you want tighter control on how and when prompt for Two Factor Authentication, you can disable it. For example, to create your own 2FA Guard or greatly modify the Login Controller. +> If you change it for your own Model, ensure it implements the `TwoFactorTotp` contract. ### Input name diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a6abd70..987fb11 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,7 +20,6 @@ - diff --git a/src/Contracts/TwoFactorAuthListener.php b/src/Contracts/TwoFactorListener.php similarity index 94% rename from src/Contracts/TwoFactorAuthListener.php rename to src/Contracts/TwoFactorListener.php index 61fc0f5..dc87cd7 100644 --- a/src/Contracts/TwoFactorAuthListener.php +++ b/src/Contracts/TwoFactorListener.php @@ -5,7 +5,7 @@ use Illuminate\Auth\Events\Validated; use Illuminate\Auth\Events\Attempting; -interface TwoFactorAuthListener +interface TwoFactorListener { /** * Saves the credentials temporarily into the class instance. diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index cc54b03..928e589 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -8,7 +8,6 @@ use Illuminate\Support\ServiceProvider; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Config\Repository; -use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthListener; class LaraguardServiceProvider extends ServiceProvider { @@ -27,6 +26,7 @@ public function register() * * @param \Illuminate\Contracts\Config\Repository $config * @param \Illuminate\Routing\Router $router + * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher * @return void */ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) @@ -38,21 +38,7 @@ public function boot(Repository $config, Router $router, Dispatcher $dispatcher) $this->loadFactoriesFrom(__DIR__ . '/../database/factories'); if ($this->app->runningInConsole()) { - $this->publishes([ - __DIR__ . '/../config/laraguard.php' => config_path('laraguard.php'), - ], 'config'); - - $this->publishes([ - __DIR__ . '/../resources/views' => resource_path('views/vendor/laraguard'), - ], 'views'); - - if (! class_exists('CreateTwoFactorAuthenticationsTable')) { - $timestamp = date('Y_m_d_His', time()); - - $this->publishes([ - __DIR__.'/../database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), - ], 'migrations'); - } + $this->publishFiles(); } } @@ -78,11 +64,39 @@ protected function registerListener(Repository $config, Dispatcher $dispatcher) return; } - $this->app->singleton(TwoFactorAuthListener::class, function ($app) use ($config) { + $this->app->singleton(Contracts\TwoFactorListener::class, function ($app) use ($config) { return new $config['laraguard.listener']($app['config'], $app['request']); }); - $dispatcher->listen(Attempting::class, $config['laraguard.listener'] . '@saveCredentials'); - $dispatcher->listen(Validated::class, $config['laraguard.listener'] . '@checkTwoFactor'); + $dispatcher->listen(Attempting::class, Contracts\TwoFactorListener::class . '@saveCredentials'); + $dispatcher->listen(Validated::class, Contracts\TwoFactorListener::class . '@checkTwoFactor'); + } + + /** + * Publish config, view and migrations files. + * + * @return void + */ + protected function publishFiles() + { + $this->publishes([ + __DIR__ . '/../config/laraguard.php' => config_path('laraguard.php'), + ], 'config'); + + $this->publishes([ + __DIR__ . '/../resources/views' => resource_path('views/vendor/laraguard'), + ], 'views'); + + // We will allow the publishing for the Two Factor Authentication migration that + // holds the TOTP data, only if it wasn't published before, avoiding multiple + // copies for the same migration, which can throw errors when re-migrating. + if (! class_exists('CreateTwoFactorAuthenticationsTable')) { + $timestamp = now()->format('Y_m_d_His'); + + $this->publishes([ + __DIR__ . + '/../database/migrations/2020_04_02_000000_create_two_factor_authentications_table.php' => database_path("/migrations/{$timestamp}_create_two_factor_authentications_table.php"), + ], 'migrations'); + } } } diff --git a/src/Listeners/EnforceTwoFactorAuth.php b/src/Listeners/EnforceTwoFactorAuth.php index 208a3cf..4e66777 100644 --- a/src/Listeners/EnforceTwoFactorAuth.php +++ b/src/Listeners/EnforceTwoFactorAuth.php @@ -6,10 +6,10 @@ use Illuminate\Auth\Events\Validated; use Illuminate\Auth\Events\Attempting; use Illuminate\Contracts\Config\Repository; -use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthListener; +use DarkGhostHunter\Laraguard\Contracts\TwoFactorListener; use DarkGhostHunter\Laraguard\Contracts\TwoFactorAuthenticatable; -class EnforceTwoFactorAuth implements TwoFactorAuthListener +class EnforceTwoFactorAuth implements TwoFactorListener { use ChecksTwoFactorCode; diff --git a/tests/Listeners/ForcesTwoFactorAuthTest.php b/tests/Listeners/ForcesTwoFactorAuthTest.php index 4353b36..68c06f2 100644 --- a/tests/Listeners/ForcesTwoFactorAuthTest.php +++ b/tests/Listeners/ForcesTwoFactorAuthTest.php @@ -2,7 +2,6 @@ namespace Tests\Listeners; -use Tests\RunsPublishableMigrations; use Tests\Stubs\UserStub; use Tests\RegistersPackage; use Illuminate\Support\Str; @@ -11,9 +10,10 @@ use Tests\CreatesTwoFactorUser; use Orchestra\Testbench\TestCase; use Tests\Stubs\UserTwoFactorStub; +use Tests\RunsPublishableMigrations; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\DatabaseMigrations; -use DarkGhostHunter\Laraguard\Listeners\EnforceTwoFactorAuth; +use DarkGhostHunter\Laraguard\Contracts\TwoFactorListener; class ForcesTwoFactorAuthTest extends TestCase { @@ -401,7 +401,7 @@ public function test_auth_requests_receives_code_and_saves_device() $this->assertCount(1, $this->user->twoFactorAuth->safe_devices); - $this->app->forgetInstance(EnforceTwoFactorAuth::class); + $this->app->forgetInstance(TwoFactorListener::class); $code = $this->user->generateRecoveryCodes()->first()['code']; From e40cb072ff2ed60074661257268313814b4945b3 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 22:27:11 -0400 Subject: [PATCH 23/24] Tiding up the service provider. --- src/LaraguardServiceProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LaraguardServiceProvider.php b/src/LaraguardServiceProvider.php index 928e589..a82a1bb 100644 --- a/src/LaraguardServiceProvider.php +++ b/src/LaraguardServiceProvider.php @@ -60,12 +60,12 @@ protected function registerMiddleware(Router $router) */ protected function registerListener(Repository $config, Dispatcher $dispatcher) { - if (! $config['laraguard.listener']) { + if (! $listener = $config['laraguard.listener']) { return; } - $this->app->singleton(Contracts\TwoFactorListener::class, function ($app) use ($config) { - return new $config['laraguard.listener']($app['config'], $app['request']); + $this->app->singleton(Contracts\TwoFactorListener::class, function ($app) use ($listener) { + return new $listener($app['config'], $app['request']); }); $dispatcher->listen(Attempting::class, Contracts\TwoFactorListener::class . '@saveCredentials'); From 5c79f2ab57ceaf0d986e0ad1272c0ba532fcbf35 Mon Sep 17 00:00:00 2001 From: DarkGhostHunter Date: Tue, 7 Apr 2020 22:49:00 -0400 Subject: [PATCH 24/24] Added title to index. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6dc7e99..bbfdff6 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This package _silently_ enables authentication using 6 digits codes, without Int * [Protecting the Login](#protecting-the-login) * [Configuration](#configuration) + [Listener](#listener) + + [Eloquent Model](#eloquent-model) + [Input name](#input-name) + [Cache Store](#cache-store) + [Recovery](#recovery) @@ -292,7 +293,7 @@ This may work wonders, but if you want tighter control on how and when prompt fo > If you change it for your own Listener, ensure it implements the `TwoFactorAuthListener` contract. -### Model +### Eloquent Model ```php return [ @@ -300,9 +301,9 @@ return [ ]; ``` -This is the model where the the Two Factor Authentication data is saved and associated to the User model. +This is the model where the the Two Factor Authentication data, like the shared secret and recovery codes, are saved and associated to the User model. -You can change this model for your own to create, validate and show the shared secret. If you plan to change where and how this is stored, you can just change the model itself or extend it. +You can change this model for your own if you wish. > If you change it for your own Model, ensure it implements the `TwoFactorTotp` contract.