Skip to content

Commit

Permalink
Adding two factor auth tests and more
Browse files Browse the repository at this point in the history
  • Loading branch information
tnylea committed May 13, 2024
1 parent d555d93 commit c24a0c4
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 111 deletions.
8 changes: 8 additions & 0 deletions public/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#auth-body{
opacity:80;
}

#auth-container{
border:0px !important;
}

#auth-heading-container{
padding-left:5px;
}
5 changes: 3 additions & 2 deletions resources/views/components/elements/heading.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
'items-end' => $heading_alignment == 'right'
])
id="auth-heading-container"
style="color:{{ config('devdojo.auth.appearance.color.text') }}"
>
<div @class([
Expand All @@ -32,8 +33,8 @@
:svgString="config('devdojo.auth.appearance.logo.svg_string')"
/>
</div>
<h1 id="auth-heading" class="mt-1 text-xl font-medium leading-9">{{ $text ?? '' }}</h1>
<h1 id="auth-heading-title" class="mt-1 text-xl font-medium leading-9">{{ $text ?? '' }}</h1>
@if(($description ?? false) && $show_subheadline)
<p class="mb-1.5 space-x-0.5 text-sm leading-5 text-center opacity-[67%]">{{ $description ?? '' }}</p>
<p id="auth-heading-description" class="mb-1.5 space-x-0.5 text-sm leading-5 text-center opacity-[67%]">{{ $description ?? '' }}</p>
@endif
</div>
1 change: 1 addition & 0 deletions resources/views/components/layouts/setup.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ class="flex absolute top-0 left-0 px-3 z-[99] ease-in-out flex-col w-screen h-s

<div class="flex flex-col w-full h-full bg-white rounded-t-md">
<div class="flex relative z-50 flex-shrink-0 justify-center items-center w-full h-10 bg-white rounded-t-md border-b border-zinc-200">
<!-- TODO - When we click the contents of the iframe it does not close this menu. When this is open, need to add a transparent layer over the iframe so this will trigger -->
<div class="relative" x-on:click.outside="previewMenuDropdown=false">
<button x-on:click="previewMenuDropdown=!previewMenuDropdown" class="flex justify-between items-center px-3 w-64 h-7 text-xs rounded-md border cursor-pointer bg-zinc-100 hover:bg-zinc-200/70">
<img src="{{ url(config('devdojo.auth.appearance.favicon.light')) }}" class="w-4 h-4 -translate-x-1.5" />
Expand Down
1 change: 1 addition & 0 deletions resources/views/components/setup/heading.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<x-phosphor-arrow-left-bold class="w-3 h-3 duration-300 ease-out translate-x-0 group-hover:-translate-x-0.5" />
<span>Back</span>
</a>
<!-- TODO - When clicking the preview we want to determine what the last page was that the user was on when this auth preview was last opened -->
<button @click="preview=true" href="/auth/setup" class="inline-flex items-center px-4 py-1.5 mb-3 space-x-1 text-sm font-medium rounded-full group bg-zinc-100 text-zinc-600 hover:text-zinc-800">
<x-phosphor-monitor-bold class="w-4 h-4 duration-300 ease-out translate-x-0 group-hover:-translate-x-0.5" />
<span>Preview</span>
Expand Down
1 change: 0 additions & 1 deletion resources/views/includes/setup/options.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<strong>Individual Screen</strong>
<span class="text-[0.6rem]">Ask for login and password on the individual screens</span>
</button>
{{-- <x-auth::setup.input type="text" label="Heading Text" x-on:keyup="document.getElementById('auth-heading').innerText = $el.value" wire:model="authData.heading" /> --}}
</div>
</div>
<div class="relative divide-y divide-zinc-200">
Expand Down
15 changes: 0 additions & 15 deletions resources/views/includes/volt-page-dynamic-middleware-name.php

This file was deleted.

96 changes: 40 additions & 56 deletions resources/views/pages/auth/login.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,6 @@
use Livewire\Volt\Component;
use Devdojo\Auth\Traits\HasConfigs;
/*$vendor_folder_location = base_path('vendors/devdojo/auth/resources/views/includes') . '/volt-page-dynamic-middleware-name.php';
$package_folder_location = base_path('packages/devdojo/auth/resources/views/includes') . '/volt-page-dynamic-middleware-name.php';
if(file_exists($package_folder_location)){
include($package_folder_location);
}
if(file_exists($vendor_folder_location)){
include($vendor_folder_location);
}*/
if(!isset($_GET['preview']) || (isset($_GET['preview']) && $_GET['preview'] != true) || !app()->isLocal()){
middleware(['guest']);
}
Expand Down Expand Up @@ -63,7 +53,6 @@ public function authenticate()
$credentials = ['email' => $this->email, 'password' => $this->password];
if(!\Auth::validate($credentials)){
$this->addError('password', trans('auth.failed'));
return;
Expand Down Expand Up @@ -93,55 +82,50 @@ public function authenticate()
return redirect()->intended('/');
}
/*$request->session()->put([
'login.id' => $user->getKey(),
'login.remember' => $request->boolean('remember'),
]);*/
}
};
?>

<x-auth::layouts.app title="{{ config('devdojo.auth.language.login.page_title') }}">
@volt('auth.login')
<x-auth::elements.container>

<x-auth::elements.heading
:text="($language->login->headline ?? 'No Heading')"
:description="($language->login->subheadline ?? 'No Description')"
:show_subheadline="($language->login->show_subheadline ?? false)" />

<form wire:submit="authenticate" class="mt-5 space-y-5">

@if($showPasswordField)
<x-auth::elements.input-placeholder value="{{ $email }}">
<button type="button" wire:click="editIdentity" class="font-medium text-blue-500">Edit</button>
</x-auth::elements.input-placeholder>
@else
<x-auth::elements.input label="Email Address" type="email" wire:model="email" autofocus="true" id="email" required />
@endif
{{-- Needs a root div for all tests to pass correctly --}}
<div>
<x-auth::layouts.app title="{{ config('devdojo.auth.language.login.page_title') }}">
@volt('auth.login')
<x-auth::elements.container>

<x-auth::elements.heading
:text="($language->login->headline ?? 'No Heading')"
:description="($language->login->subheadline ?? 'No Description')"
:show_subheadline="($language->login->show_subheadline ?? false)" />

<form wire:submit="authenticate" class="mt-5 space-y-5">

@if($showPasswordField)
<x-auth::elements.input-placeholder value="{{ $email }}">
<button type="button" wire:click="editIdentity" class="font-medium text-blue-500">Edit</button>
</x-auth::elements.input-placeholder>
@else
<x-auth::elements.input label="Email Address" type="email" wire:model="email" autofocus="true" id="email" required />
@endif

@if($showPasswordField)
<x-auth::elements.input label="Password" type="password" wire:model="password" id="password" />
<div class="flex justify-between items-center mt-6 text-sm leading-5">
<x-auth::elements.text-link href="{{ route('auth.password.request') }}">Forgot your password?</x-auth::elements.text-link>
</div>
@endif

<x-auth::elements.button type="primary" rounded="md" size="md" submit="true">Continue</x-auth::elements.button>
</form>


@if($showPasswordField)
<x-auth::elements.input label="Password" type="password" wire:model="password" id="password" />
<div class="flex justify-between items-center mt-6 text-sm leading-5">
<x-auth::elements.text-link href="{{ route('auth.password.request') }}">Forgot your password?</x-auth::elements.text-link>
</div>
@endif

<x-auth::elements.button type="primary" rounded="md" size="md" submit="true">Continue</x-auth::elements.button>
</form>


<div class="mt-3 space-x-0.5 text-sm leading-5 text-left" style="color:{{ config('devdojo.auth.appearance.color.text') }}">
<span class="opacity-[47%]">Don't have an account?</span>
<x-auth::elements.text-link href="{{ route('auth.register') }}">Sign up</x-auth::elements.text-link>
</div>

</x-auth::elements.container>
@endvolt
</x-auth::layouts.app>
<div class="mt-3 space-x-0.5 text-sm leading-5 text-left" style="color:{{ config('devdojo.auth.appearance.color.text') }}">
<span class="opacity-[47%]">Don't have an account?</span>
<x-auth::elements.text-link href="{{ route('auth.register') }}">Sign up</x-auth::elements.text-link>
</div>

</x-auth::elements.container>
@endvolt
</x-auth::layouts.app>
</div>
1 change: 1 addition & 0 deletions resources/views/pages/auth/register.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public function register()
?>

{{-- Needs a root div for all tests to pass correctly --}}
<div>
<x-auth::layouts.app title="{{ config('devdojo.auth.language.register.page_title') }}">

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
name('user.two-factor-authentication');
//middleware(['auth', 'verified', 'password.confirm']);
middleware(['auth', 'verified']);
middleware(['auth', 'verified', 'two-factor-enabled']);
// middleware(['auth'])
new class extends Component
Expand Down
2 changes: 2 additions & 0 deletions src/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PragmaRX\Google2FA\Google2FA;
use Illuminate\Support\Facades\Route;
use Devdojo\Auth\Http\Middleware\TwoFactorChallenged;
use Devdojo\Auth\Http\Middleware\TwoFactorEnabled;

class AuthServiceProvider extends ServiceProvider
{
Expand All @@ -21,6 +22,7 @@ public function boot()
{

Route::middlewareGroup('two-factor-challenged', [TwoFactorChallenged::class]);
Route::middlewareGroup('two-factor-enabled', [TwoFactorEnabled::class]);

/*
* Optional methods to load your package assets
Expand Down
23 changes: 23 additions & 0 deletions src/Http/Middleware/TwoFactorEnabled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Devdojo\Auth\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class TwoFactorEnabled
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (!config('devdojo.auth.settings.enable_2fa')) {
return redirect('/');
}
return $next($request);
}
}
84 changes: 51 additions & 33 deletions tests/Feature/TwoFactorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Session;

beforeEach(function () {
// Ensure each test starts with a clean slate
Expand All @@ -19,41 +20,58 @@
->assertRedirect('auth/login');
});

test('when user logs in and two factor auth is active, they will have the login.id session created', function(){
$user = User::factory()->create(['name' => 'Homer Simpson', 'email' => '[email protected]', 'password' => \Hash::make('DuffBeer123')]);
// $this->get('auth/login')
// ->seeInField('email', '[email protected]')
// ->click('.auth-component-button')
// ->assertSee('Password');
// dd($user->two_factor_secret);
//dd(\Schema::getColumnListing('users'));
//dd(env('DB_CONNECTION'));
//dd($user);
})->todo();

it('user can view two factor challenge page after they login', function(){

// Livewire::test('auth.register')
// ->set('email', '[email protected]')
// ->set('password', 'secret1234')
// ->set('name', 'John Doe')
// ->call('register')

withANewUser()->get('auth/two-factor-challenge')->asertOK();
// $user = loginAsUser(null);
// Livewire::test('auth.two-factor-challenge');
// ->assertSee('When you enabled 2FA');
})->todo();

test('when authenticated, user can view /user/two-factor-authentication page', function(){

test('User logs in when two factor disabled, the login.id session should not be created', function(){
$user = createUser(['password' => \Hash::make('password123'), 'two_factor_confirmed_at' => now()]);

})->todo();
Livewire::test('auth.login')
->set('email', $user->email)
->set('showPasswordField', true)
->set('password', 'password123')
->call('authenticate')
->assertHasNoErrors()
->assertRedirect('/');

$this->assertTrue(!Session::has('login.id'));
});

test('when authenticated, user can view /user/two-factor-authentication page and they can click enable and add auth code', function(){
test('User logs in when two factor enabled, the login.id session should be created', function(){
config()->set('devdojo.auth.settings.enable_2fa', true);
$user = createUser(['password' => \Hash::make('password123'), 'two_factor_confirmed_at' => now()]);

})->todo();
Livewire::test('auth.login')
->set('email', $user->email)
->set('showPasswordField', true)
->set('password', 'password123')
->call('authenticate')
->assertHasNoErrors()
->assertRedirect('auth/two-factor-challenge');

$this->assertTrue(Session::has('login.id'));
});

// scenarios when 2FA is disabled by application admin
test('if two factor auth is disabled, user can login with name and password and they will not be redirected to 2fa page, even if they have the correct two_factor table columns filled', function(){
test('User logs in without 2FA, they should not be redirected to auth/two-factor-challenge page', function(){
config()->set('devdojo.auth.settings.enable_2fa', true);
$user = createUser(['password' => \Hash::make('password123')]);

})->todo();
Livewire::test('auth.login')
->set('email', $user->email)
->set('showPasswordField', true)
->set('password', 'password123')
->call('authenticate')
->assertHasNoErrors()
->assertRedirect('/');
});

it('user cannot view two factor challenge page logging in if it\'s disabled', function(){
$user = loginAsUser();
$this->get('user/two-factor-authentication')
->assertRedirect('/');
});

it('user can view two factor challenge page when it\'s enabled', function(){
config()->set('devdojo.auth.settings.enable_2fa', true);
$user = loginAsUser();
$this->get('user/two-factor-authentication')
->assertOk();
});
10 changes: 7 additions & 3 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ function something()

use App\Models\User;

function loginAsUser(User $user = null){
$user = $user ?? User::factory()->create();
function loginAsUser(User $user = null, $data = []){
$user = $user ?? User::factory()->create($data);
test()->actingAs($user);

return $user;
}

function createUser($data){
return User::factory()->create($data);
}

function withANewUser(){
return test()->actingAs(User::factory()->create());
}
}

0 comments on commit c24a0c4

Please sign in to comment.