Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: 2FA #256

Merged
merged 33 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9053250
install fortify
franbarbalopez Apr 24, 2024
3cfeec0
add trait to user model
franbarbalopez Apr 24, 2024
fda4d12
created views and livewire component
franbarbalopez Apr 24, 2024
4e01660
lint
franbarbalopez Apr 24, 2024
53b32c7
fix failing tests
franbarbalopez Apr 24, 2024
3eb834d
delete unused code
franbarbalopez Apr 24, 2024
e88cb4e
phpstan fix
franbarbalopez Apr 24, 2024
e6c98a9
redirect after login from profile to feed
franbarbalopez Apr 24, 2024
63dee2b
deleting two factor confirmation and testing component
franbarbalopez Apr 25, 2024
c1eb758
fix lock
MrPunyapal Jul 20, 2024
7a7e62d
Add two-factor authentication secret property to User model
MrPunyapal Jul 29, 2024
8f7f599
chore: Refactor TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
06a15f1
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
31ccf88
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
06a9aaa
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
b2d3021
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
fdf4385
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
add000c
refactor: Enable two-factor authentication confirmation in Fortify co…
MrPunyapal Jul 29, 2024
78f0a37
refactor: Remove two-factor authentication secret property from User …
MrPunyapal Jul 29, 2024
f5c5346
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
18c92a3
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
9ea743b
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 29, 2024
baf59bf
refactor: Rename TwoFactorAuthenticationTest.php to TwoFactorAuthenti…
MrPunyapal Jul 29, 2024
f73f76e
refactor: Add #[Locked] attribute to showingConfirmation property in …
MrPunyapal Jul 30, 2024
6d94f5b
refactor: Improve TwoFactorAuthenticationForm component for getting b…
MrPunyapal Jul 30, 2024
ee916c0
refactor: Add ConfirmsPasswords trait for password confirmation in Tw…
MrPunyapal Jul 30, 2024
47d1a32
refactor: Add ConfirmsPasswords trait for password confirmation in Tw…
MrPunyapal Jul 30, 2024
ce252db
refactor: Add ConfirmPassword component for password confirmation in …
MrPunyapal Jul 30, 2024
2362149
refactor: Add ConfirmPasswordTest for password confirmation in TwoFac…
MrPunyapal Jul 30, 2024
900d631
refactor: Add ConfirmPassword component for password confirmation in …
MrPunyapal Jul 30, 2024
b6c958a
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 30, 2024
dd1d3ef
refactor: Improve TwoFactorAuthenticationForm component
MrPunyapal Jul 30, 2024
91b320e
refactor: Add two-factor authentication properties to User model
MrPunyapal Jul 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions app/Http/Controllers/Auth/AuthenticatedSessionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

namespace App\Http\Controllers\Auth;

use App\Http\Requests\Auth\LoginRequest;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

final readonly class AuthenticatedSessionController
Expand All @@ -20,22 +17,6 @@ public function create(): View
return view('auth.login');
}

/**
* Handle an incoming authentication request.
*/
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();

session()->regenerate();

$user = type($request->user())->as(User::class);

return redirect()->intended(route('profile.show', [
'username' => $user->username,
], absolute: false));
}

/**
* Destroy an authenticated session.
*/
Expand Down
88 changes: 0 additions & 88 deletions app/Http/Requests/Auth/LoginRequest.php

This file was deleted.

58 changes: 58 additions & 0 deletions app/Livewire/Auth/ConfirmPassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Auth;

use Illuminate\View\View;
use Livewire\Attributes\Locked;
use Livewire\Attributes\On;
use Livewire\Component;

final class ConfirmPassword extends Component
{
/**
* The Password being confirmed.
*/
public string $password;

/**
* The ID to confirm.
*/
#[Locked]
public string $idToConfirm;

/**
* Initialize the confirmation.
*/
#[On('confirm-password')]
public function initialize(string $idToConfirm): void
{
$this->idToConfirm = $idToConfirm;
$this->password = '';
$this->dispatch('open-modal', 'confirm-password');
}

/**
* Confirm the password.
*/
public function confirm(): void
{
$this->validate([
'password' => ['required', 'string', 'current_password'],
]);

session()->put('auth.password_confirmed_at', time());
$this->password = '';
$this->dispatch('close-modal', 'confirm-password');
$this->dispatch('password-confirmed-'.$this->idToConfirm);
}

/**
* Render the component.
*/
public function render(): View
{
return view('livewire.auth.confirm-password');
}
}
32 changes: 32 additions & 0 deletions app/Livewire/Concerns/ConfirmsPasswords.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Concerns;

trait ConfirmsPasswords
{
/**
* Ensure that the user's password has been recently confirmed.
*/
protected function ensurePasswordIsConfirmed(string $idToConfirm, ?int $maximumSecondsSinceConfirmation = null): bool
{
if ($this->passwordIsConfirmed($maximumSecondsSinceConfirmation)) {
return true;
}

$this->dispatch('confirm-password', idToConfirm: $idToConfirm);

return false;
}

/**
* Determine if the user's password has been recently confirmed.
*/
protected function passwordIsConfirmed(?int $maximumSecondsSinceConfirmation = null): bool
{
$maximumSecondsSinceConfirmation = $maximumSecondsSinceConfirmation !== null && $maximumSecondsSinceConfirmation !== 0 ? $maximumSecondsSinceConfirmation : config('auth.password_timeout', 900);

return (time() - session('auth.password_confirmed_at', 0)) < $maximumSecondsSinceConfirmation;
}
}
155 changes: 155 additions & 0 deletions app/Livewire/Profile/TwoFactorAuthenticationForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Profile;

use App\Livewire\Concerns\ConfirmsPasswords;
use App\Models\User;
use Illuminate\View\View;
use Laravel\Fortify\Actions\ConfirmTwoFactorAuthentication;
use Laravel\Fortify\Actions\DisableTwoFactorAuthentication;
use Laravel\Fortify\Actions\EnableTwoFactorAuthentication;
use Laravel\Fortify\Actions\GenerateNewRecoveryCodes;
use Livewire\Attributes\Locked;
use Livewire\Attributes\On;
use Livewire\Component;

final class TwoFactorAuthenticationForm extends Component
{
use ConfirmsPasswords;

/**
* Indicates if two factor authentication QR code is being displayed.
*/
#[Locked]
public bool $showingQrCode = false;

/**
* Indicates if two factor authentication recovery codes are being displayed.
*/
#[Locked]
public bool $showingRecoveryCodes = false;

/**
* Indicates if the two factor authentication confirmation input and button are being displayed.
*/
#[Locked]
public bool $showingConfirmation = false;

/**
* Determine if two factor authentication is enabled.
*/
#[Locked]
public bool $enabled = false;

/**
* The OTP code for confirming two factor authentication.
*/
public ?string $code = null;

/**
* Mount the component.
*/
public function mount(): void
{
$user = type(auth()->user())->as(User::class);
$this->enabled = $user->hasEnabledTwoFactorAuthentication();
}

/**
* Enable two factor authentication for the user.
*/
#[On('password-confirmed-enable-two-factor-authentication')]
public function enableTwoFactorAuthentication(EnableTwoFactorAuthentication $enable): void
{
if (! $this->ensurePasswordIsConfirmed('enable-two-factor-authentication')) {
return;
}

$enable(auth()->user());
$this->showingQrCode = true;
$this->showingConfirmation = true;
$this->enabled = true;
}

/**
* Confirm two factor authentication for the user.
*/
public function confirmTwoFactorAuthentication(ConfirmTwoFactorAuthentication $confirm): void
{
$confirm(auth()->user(), $this->pull('code'));

$this->showingQrCode = false;
$this->showingConfirmation = false;
$this->showingRecoveryCodes = true;
}

/**
* Display the user's recovery codes.
*/
#[On('password-confirmed-show-recovery-codes')]
public function showRecoveryCodes(): void
{
if (! $this->enabled) {
return;
}

if (! $this->ensurePasswordIsConfirmed('show-recovery-codes')) {
return;
}

$this->showingRecoveryCodes = true;
}

/**
* Generate new recovery codes for the user.
*/
#[On('password-confirmed-generate-new-recovery-codes')]
public function regenerateRecoveryCodes(GenerateNewRecoveryCodes $generate): void
{
if (! $this->enabled) {
return;
}

if (! $this->ensurePasswordIsConfirmed('generate-new-recovery-codes')) {
return;
}

$generate(auth()->user());

$this->showingRecoveryCodes = true;
}

/**
* Disable two factor authentication for the user.
*/
#[On('password-confirmed-disable-two-factor-authentication')]
public function disableTwoFactorAuthentication(DisableTwoFactorAuthentication $disable): void
{
if (! $this->enabled) {
return;
}

if (! $this->ensurePasswordIsConfirmed('disable-two-factor-authentication')) {
return;
}

$disable(auth()->user());

$this->showingQrCode = false;
$this->showingRecoveryCodes = false;
$this->enabled = false;
$this->code = null;
}

/**
* Render the component.
*/
public function render(): View
{
return view('livewire.profile.two-factor-authentication-form', [
'user' => auth()->user(),
]);
}
}
Loading