diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index a2dbce2..791b25b 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -26,7 +26,7 @@ @if(config('devdojo.auth.settings.enable_branding') && !app()->isLocal()) - +

Secured by DevDojo

diff --git a/resources/views/pages/auth/password/[token].blade.php b/resources/views/pages/auth/password/[token].blade.php index 5d50612..45e76ff 100644 --- a/resources/views/pages/auth/password/[token].blade.php +++ b/resources/views/pages/auth/password/[token].blade.php @@ -6,9 +6,7 @@ use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Password; use Illuminate\Auth\Events\PasswordReset; - -use function Laravel\Folio\name; - +use function Laravel\Folio\{middleware, name}; use Livewire\Volt\Component; use Livewire\Attributes\Validate; @@ -78,10 +76,10 @@ function ($user, $password) { />
- - - - Reset password + + + + Reset password @endvolt diff --git a/resources/views/pages/auth/password/confirm.blade.php b/resources/views/pages/auth/password/confirm.blade.php index 3e961ab..62c0b65 100644 --- a/resources/views/pages/auth/password/confirm.blade.php +++ b/resources/views/pages/auth/password/confirm.blade.php @@ -1,10 +1,14 @@ isLocal()){ + middleware('auth'); +} + name('password.confirm'); new class extends Component @@ -40,8 +44,8 @@ public function confirm() :show_subheadline="($language->passwordConfirm->show_subheadline ?? false)" />
- - Confirm password + + Confirm password @endvolt diff --git a/resources/views/pages/auth/password/reset.blade.php b/resources/views/pages/auth/password/reset.blade.php index 37647f3..33273ff 100644 --- a/resources/views/pages/auth/password/reset.blade.php +++ b/resources/views/pages/auth/password/reset.blade.php @@ -2,7 +2,7 @@ use Devdojo\Auth\Traits\HasConfigs; use Illuminate\Support\Facades\Password; -use function Laravel\Folio\name; +use function Laravel\Folio\{middleware, name}; use Livewire\Volt\Component; use Livewire\Attributes\Validate; @@ -68,13 +68,13 @@ public function sendResetPasswordLink() @else
- - Send password reset link + + Send password reset link @endif
Or - return to login + return to login
@endvolt diff --git a/resources/views/pages/user/two-factor-authentication/index.blade.php b/resources/views/pages/user/two-factor-authentication/index.blade.php index e5e0ddd..a95d08b 100644 --- a/resources/views/pages/user/two-factor-authentication/index.blade.php +++ b/resources/views/pages/user/two-factor-authentication/index.blade.php @@ -128,7 +128,7 @@ public function disable(){

Two factor authentication disabled.

When you enabled 2FA, you will be prompted for a secure code during authentication. This code can be retrieved from your phone's Google Authenticator application.

- Enable + Enable
@else diff --git a/routes/web.php b/routes/web.php index f77adf1..a383fc9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -28,15 +28,6 @@ // Add social routes Route::get('auth/{driver}/redirect', [SocialController::class, 'redirect']); Route::get('auth/{driver}/callback', [SocialController::class, 'callback']); -}); - -Route::get('hey', function () { - $rowsArray = []; - $socialProviders = config('devdojo.auth.providers', []); - foreach ($socialProviders as $key => $provider) { - $provider['slug'] = $key; - array_push($rowsArray, $provider); - } - dd($rowsArray); }); + diff --git a/src/AuthServiceProvider.php b/src/AuthServiceProvider.php index fe8d988..fc74f24 100644 --- a/src/AuthServiceProvider.php +++ b/src/AuthServiceProvider.php @@ -80,6 +80,7 @@ public function boot(): void } $this->handleStarterKitFunctionality(); + $this->loadDynamicRoutesForTesting(); } private function registerAuthFolioDirectory(): void @@ -150,4 +151,13 @@ public function register() $this->app->register(\Devdojo\Auth\Providers\DuskServiceProvider::class); } } + + private function loadDynamicRoutesForTesting() + { + if (app()->environment('testing') || app()->environment('local')) { + Route::get('/auth/password_confirmation_test', function () { + return 'Test Confirmed'; + })->middleware('web', 'auth', 'password.confirm'); + } + } } diff --git a/src/Providers/DuskServiceProvider.php b/src/Providers/DuskServiceProvider.php index 367ab45..a27f63a 100644 --- a/src/Providers/DuskServiceProvider.php +++ b/src/Providers/DuskServiceProvider.php @@ -45,6 +45,18 @@ public function boot(): void return $this; }); + Browser::macro('loginAsJohnDoe', function(){ + $this->loginAs(\Devdojo\Auth\Models\User::where('email', 'johndoe@gmail.com')->first()); + return $this; + }); + + Browser::macro('enable2FAforJohnDoe', function(){ + $johnDoe = \Devdojo\Auth\Models\User::where('email', 'johndoe@gmail.com')->first(); + $johnDoe->two_factor_confirmed_at = now(); + $johnDoe->save(); + return $this; + }); + Browser::macro('assertRedirectAfterAuthUrlIsCorrect', function () { $redirectExpectedToBe = '/'; if (class_exists(\Devdojo\Genesis\Genesis::class)) { @@ -67,5 +79,18 @@ public function boot(): void return $this; }); + + Browser::macro('typeAndSubmit', function (?string $selector, string $value) { + $this->type($selector, $value) + ->click('@submit-button'); + + return $this; + }); + + Browser::macro('visitPasswordConfirmTestPage', function () { + $this->visit('/auth/password_confirmation_test'); + return $this; + }); + } } diff --git a/tests/Browser/ConfirmPasswordTest.php b/tests/Browser/ConfirmPasswordTest.php new file mode 100644 index 0000000..ac6aee3 --- /dev/null +++ b/tests/Browser/ConfirmPasswordTest.php @@ -0,0 +1,55 @@ +browse(function (Browser $browser) { + $browser + ->visit(new ConfirmPassword) + ->assertPathIs('/auth/login'); + }); +}); + +test('Password Confirm Protected Page Redirects', function () { + $browser = $this->browse(function (Browser $browser) { + $browser + ->visitPasswordConfirmTestPage() + ->assertPathIs('/auth/login') + ->createJohnDoe() + ->loginAsJohnDoe() + ->visitPasswordConfirmTestPage() + ->assertPathIs('/auth/password/confirm'); + }); +}); + +test('Password Confirm Works', function(){ + $browser = $this->browse(function (Browser $browser) { + $browser + ->createJohnDoe() + ->loginAsJohnDoe() + ->visit(new ConfirmPassword) + ->type('@password-input', 'password') + ->clickAndWaitForReload('@submit-button') + ->assertRedirectAfterAuthUrlIsCorrect(); + }); +}); + +test('Password Confirm Works and redirect to password protected page', function(){ + $browser = $this->browse(function (Browser $browser) { + $browser + ->createJohnDoe() + ->loginAsJohnDoe() + ->visitPasswordConfirmTestPage() + ->type('@password-input', 'password') + ->clickAndWaitForReload('@submit-button') + ->assertSee('Test Confirmed'); + }); +}); \ No newline at end of file diff --git a/tests/Browser/ForgotPasswordTest.php b/tests/Browser/ForgotPasswordTest.php new file mode 100644 index 0000000..67682bc --- /dev/null +++ b/tests/Browser/ForgotPasswordTest.php @@ -0,0 +1,73 @@ +browse(function (Browser $browser) { + $browser + ->visit(new PasswordResetRequest) + ->authAttributeRemove('#email', 'required') + ->testValidationErrorOnSubmit('The email field is required'); + }); +}); + +test('Invalid Email Validation', function () { + $browser = $this->browse(function (Browser $browser) { + $browser + ->visit(new PasswordResetRequest) + ->authAttributeChange('#email', 'type', 'text') + ->type('@email-input', 'johndoe') + ->testValidationErrorOnSubmit('The email field must be a valid email address'); + }); +}); + +test('Email Does Not Exist', function () { + $browser = $this->browse(function (Browser $browser) { + $browser + ->visit(new PasswordResetRequest) + ->type('@email-input', 'jimmycrackcorn@gmail.com') + ->testValidationErrorOnSubmit('We can\'t find a user with that email address'); + }); +}); + +test('Email reset functionality', function () { + $browser = $this->browse(function (Browser $browser) { + + $browser + ->visit(new PasswordResetRequest) + ->createJohnDoe() + ->clearLogFile() + ->typeAndSubmit('@email-input', 'johndoe@gmail.com') + ->waitForText('We have emailed your password reset link') + ->getLogFile(function ($content) use ($browser) { + + $foundLine = $this->findLineContainingSubstring($content, 'Reset Password:'); + $url = str_replace('Reset Password: ', '', $foundLine); + $browser + ->visit($url) + ->assertSeeIn('#auth-heading-title', 'Reset Password') + ->type('@password-input', 'password123') + ->type('@password-confirm-input', 'password123') + ->clickAndWaitForReload('@submit-button') + ->assertRedirectAfterAuthUrlIsCorrect(); + }); + }); +}); + +test('Link to Login Page', function () { + $browser = $this->browse(function (Browser $browser) { + $browser + ->visit(new PasswordResetRequest) + ->click('@login-link') + ->waitFor('@auth-login') + ->assertPathIs('/auth/login'); + }); +}); \ No newline at end of file diff --git a/tests/Browser/LoginTest.php b/tests/Browser/LoginTest.php index 0d2ac63..40f0dbf 100644 --- a/tests/Browser/LoginTest.php +++ b/tests/Browser/LoginTest.php @@ -10,7 +10,8 @@ $this->browse(function (Browser $browser) { $browser->visit(new Login) ->createJohnDoe() - ->loginAsJohnDoe(); + ->formLoginAsJohnDoe() + ->assertRedirectAfterAuthUrlIsCorrect(); }); }); diff --git a/tests/Browser/LogoutTest.php b/tests/Browser/LogoutTest.php new file mode 100644 index 0000000..e69de29 diff --git a/tests/Browser/Pages/ConfirmPassword.php b/tests/Browser/Pages/ConfirmPassword.php new file mode 100644 index 0000000..7b1a8e9 --- /dev/null +++ b/tests/Browser/Pages/ConfirmPassword.php @@ -0,0 +1,16 @@ +visit('/auth/login') @@ -23,17 +23,9 @@ public function loginAsJohnDoe(Browser $browser) ->click('@submit-button') ->waitFor('@password-input') ->type('@password-input', 'password') - ->clickAndWaitForReload('@submit-button') - ->assertRedirectAfterAuthUrlIsCorrect(); + ->clickAndWaitForReload('@submit-button'); return $this; } - public function typeAndSubmit(Browser $browser, $selector, $value) - { - $browser->type($selector, $value) - ->click('@submit-button'); - - return $this; - } } diff --git a/tests/Browser/Pages/PasswordResetRequest.php b/tests/Browser/Pages/PasswordResetRequest.php new file mode 100644 index 0000000..3aa986f --- /dev/null +++ b/tests/Browser/Pages/PasswordResetRequest.php @@ -0,0 +1,16 @@ +browse(function (Browser $browser) { + $browser->visit(new TwoFactorAuth) + ->assertPathIs('/auth/login'); + }); +}); + +test('Successfully View 2FA Setup Page', function () { + + $this->browse(function (Browser $browser) { + $browser + ->createJohnDoe() + ->loginAsJohnDoe() + ->visit(new TwoFactorAuth) + ->assertSee('Two factor authentication disabled') + ->click('@enable-button') + ->waitForText('Finish enabling two factor authentication') + ->assertSee('Finish enabling two factor authentication'); + }); +}); + diff --git a/tests/Browser/TwoFactorChallengeTest.php b/tests/Browser/TwoFactorChallengeTest.php new file mode 100644 index 0000000..f2ead27 --- /dev/null +++ b/tests/Browser/TwoFactorChallengeTest.php @@ -0,0 +1,29 @@ +browse(function (Browser $browser) { + $browser->visit(new TwoFactorChallenge) + ->assertPathIs('/auth/login'); + }); +}); + +test('User logs in with 2fa enabled and redirected to challenge', function () { + // Turn on 2FA site-wide + $this->setConfig('devdojo.auth.settings.enable_2fa', true); + $this->browse(function (Browser $browser) { + $browser->visit(new Login) + ->createJohnDoe() + ->enable2FAforJohnDoe() + ->formLoginAsJohnDoe() + ->assertPathIs('/auth/two-factor-challenge'); + + $browser->pause(2000); + }); +}); \ No newline at end of file