Skip to content

Commit 16654b1

Browse files
committed
Security: 2fa migrated to livewire
1 parent a460b36 commit 16654b1

File tree

7 files changed

+167
-72
lines changed

7 files changed

+167
-72
lines changed

app/Http/Controllers/PasswordSecurityController.php

-42
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Http\Controllers;
44

5-
use App\Http\Requests\Settings\UserEnableTfa;
65
use App\Models\PasswordSecurity;
76
use App\Models\User;
87
use Illuminate\Http\Request;
@@ -40,47 +39,6 @@ public function generate2faSecretCode(Request $request)
4039
return redirect()->route('settings.account')->with('success', __('settings.account.2fa.success_key'));
4140
}
4241

43-
/*
44-
* Enables 2fa for the current user.
45-
*/
46-
public function enable2fa(UserEnableTfa $request)
47-
{
48-
/** @var User $user */
49-
$user = $request->user();
50-
51-
// Enable OTP if the Authenticator code matches secret
52-
$otp = new Google2FA;
53-
$secret = $request->input('otp');
54-
$valid = $otp->verifyKey($user->passwordSecurity->google2fa_secret, $secret);
55-
56-
// If OTP code is valid enable OTP
57-
if ($valid) {
58-
$user->passwordSecurity->update(['google2fa_enable' => 1]);
59-
// 2FA is enabled, log out the user and ask them to set up.
60-
auth()->logout();
61-
session()->flush();
62-
63-
return redirect()->route('login')->with('success', __('settings.account.2fa.success_enable'));
64-
}
65-
66-
return redirect()->route('settings.account')->with('error', __('settings.account.2fa.error_enable'));
67-
}
68-
69-
/*
70-
* Disables 2fa for the current user.
71-
*/
72-
public function disable2fa(Request $request)
73-
{
74-
/** @var User $user */
75-
$user = $request->user();
76-
77-
// Update disabling OTP
78-
$user->passwordSecurity->google2fa_enable = 0;
79-
$user->passwordSecurity->save();
80-
81-
return redirect()->route('settings.account')->with('success', __('settings.account.2fa.success_disable'));
82-
}
83-
8442
/*
8543
* Aborts login of user with 2fa enabled.
8644
*/

app/Http/Requests/Settings/UserEnableTfa.php

-18
This file was deleted.

app/Livewire/Users/Otp.php

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
namespace App\Livewire\Users;
4+
5+
use Livewire\Attributes\Validate;
6+
use Livewire\Component;
7+
use App\Models\PasswordSecurity;
8+
use App\Models\User;
9+
use PragmaRX\Google2FA\Google2FA;
10+
11+
class Otp extends Component
12+
{
13+
public $duplicates;
14+
public bool $clickedBefore = false;
15+
protected $listeners = ['refreshTable' => '$refresh']; // Listen for table refresh event
16+
17+
#[Validate('required|numeric')]
18+
public string $otp = '';
19+
20+
/*
21+
* Generates secret code for 2fa
22+
*/
23+
public function generate2faSecretCode()
24+
{
25+
/** @var User $user */
26+
$user = auth()->user();
27+
28+
$otp = new Google2FA;
29+
30+
// Generate a new Google2FA code for User
31+
PasswordSecurity::create([
32+
'user_id' => $user->id,
33+
'google2fa_enable' => 0,
34+
'google2fa_secret' => $otp->generateSecretKey(),
35+
]);
36+
$this->dispatch('refreshTable');
37+
}
38+
39+
/*
40+
* Enables 2fa for the current user.
41+
*/
42+
public function enable2fa()
43+
{
44+
$this->validate();
45+
46+
/** @var User $user */
47+
$user = auth()->user();
48+
49+
// Enable OTP if the Authenticator code matches secret
50+
$otpModel = new Google2FA;
51+
$valid = $otpModel->verifyKey($user->passwordSecurity->google2fa_secret, $this->otp);
52+
53+
// If OTP code is valid enable OTP
54+
if ($valid) {
55+
$user->passwordSecurity->update(['google2fa_enable' => 1]);
56+
// 2FA is enabled, log out the user and ask them to set up.
57+
auth()->logout();
58+
session()->flush();
59+
60+
$this->redirectRoute('login', ['success' => __('settings.account.2fa.success_enable')]);
61+
}
62+
session()->flash('otp-error', __('settings.account.2fa.error_enable'));
63+
64+
}
65+
66+
/*
67+
* Disables 2fa for the current user.
68+
*/
69+
public function disable2fa()
70+
{
71+
if ($this->clickedBefore) {
72+
/** @var User $user */
73+
$user = auth()->user();
74+
75+
// Update disabling OTP
76+
$user->passwordSecurity->google2fa_enable = 0;
77+
$user->passwordSecurity->save();
78+
session()->flash('disable-success', __('settings.account.2fa.success_disable'));
79+
} else {
80+
$this->clickedBefore = true;
81+
}
82+
83+
84+
}
85+
86+
public function render()
87+
{
88+
89+
/** @var User $user */
90+
$user = auth()->user();
91+
92+
return view('livewire.users.otp', ['user' => $user]);
93+
}
94+
}

lang/en/settings.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
'account' => [
55
'2fa' => [
66
'actions' => [
7-
'disable' => 'Disable two-factor authentication',
8-
'finish' => 'Finish setup and log in',
7+
'disable' => 'Disable two-factor authentication',
8+
'disable-confirm' => 'Click again to confirm',
9+
'finish' => 'Finish setup and log in',
910
],
1011
'activation_helper' => 'To finish setting up your account\'s two-factor authentication, please follow these instructions.',
1112
'disable' => [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<div>
2+
<x-box class="mb-12 rounded-2xl">
3+
<x-slot name="title">
4+
{{ __('settings.account.2fa.title') }}
5+
</x-slot>
6+
7+
@if (session()->has('disable-success'))
8+
<span class="text-green-600">
9+
{{ session('disable-success') }}
10+
</span>
11+
@endif
12+
13+
@if ($user->passwordSecurity?->google2fa_enable)
14+
<p class="hep-block">{{ __('settings.account.2fa.enabled') }}</p>
15+
16+
<div class="text-right">
17+
<button wire:click="disable2fa" class="btn2 btn-error"> @if ($clickedBefore) {{ __('settings.account.2fa.actions.disable-confirm') }} @else {{ __('settings.account.2fa.actions.disable') }} @endif </button>
18+
</div>
19+
@else
20+
@if(auth()->user()->isSocialLogin())
21+
<p>{{ __('settings.account.2fa.social') }}</p>
22+
@elseif(empty($user->passwordSecurity))
23+
<p>
24+
{{ __('settings.account.2fa.helper') }} <a href="https://docs.kanka.io/en/latest/account/security/two-factor-authentication.html">{{ __('settings.account.2fa.learn_more') }}</a>
25+
</p>
26+
27+
<p>{!! __('settings.account.2fa.enable_instructions', [
28+
'android' => '<a target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Android</a>',
29+
'ios' => '<a target="_blank" href="https://apps.apple.com/us/app/google-authenticator/id388497605">iOS</a>',
30+
]) !!}</p>
31+
32+
<div class="text-right">
33+
<button wire:click="generate2faSecretCode" class="btn2 btn-primary"> {{ __('settings.account.2fa.generate_qr') }} </button>
34+
</div>
35+
@elseif(!$user->passwordSecurity->google2fa_enable)
36+
<div>
37+
<x-grid type="1/1">
38+
<p>{{ __('settings.account.2fa.activation_helper') }}</p>
39+
40+
<x-forms.field field="qr-code" required :label="__('settings.account.2fa.fields.qrcode')">
41+
{!! $user->passwordSecurity->getGoogleQR() !!}
42+
</x-forms.field>
43+
</x-grid>
44+
<div class="flex flex-wrap gap-2 justify-between items-end">
45+
<div class="field field-name">
46+
<label>{{__('settings.account.2fa.fields.otp')}}</label>
47+
48+
<input type="password" wire:model="otp" name="otp" maxlength="12" class="input rounded text-dark w-full p-2" />
49+
<div>
50+
@error('otp') <span class="text-error">{{ $message }}</span> @enderror
51+
</div>
52+
@if (session()->has('otp-error'))
53+
<span class="text-error">
54+
{{ session('otp-error') }}
55+
</span>
56+
@endif
57+
</div>
58+
<div class="text-right">
59+
<button wire:click="enable2fa" class="btn2 btn-primary"> {{ __('settings.account.2fa.actions.finish') }} </button>
60+
</div>
61+
</div>
62+
</div>
63+
@endif
64+
@endif
65+
</x-box>
66+
</div>
67+

resources/views/settings/account.blade.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@
5353
</div>
5454
</div>
5555
</x-box>
56-
57-
@includeWhen(config('google2fa.enabled'), 'settings._tfa')
56+
@if (config('google2fa.enabled'))
57+
@livewire('users.otp')
58+
@endif
5859

5960
<x-box class="border-error border rounded-2xl">
6061
<x-slot name="title">

routes/settings.php

-8
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,6 @@
136136
Route::post('/security/generate2faSecret', [PasswordSecurityController::class, 'generate2faSecretCode'])
137137
->name('settings.security.generate-2fa');
138138

139-
// Enable 2FA for User
140-
Route::post('/security/enable2fa', [PasswordSecurityController::class, 'enable2fa'])
141-
->name('settings.security.enable-2fa');
142-
143-
// Disable 2FA for User
144-
Route::post('/security/disable2fa', [PasswordSecurityController::class, 'disable2fa'])
145-
->name('settings.security.disable-2fa');
146-
147139
// Verify 2FA if User has it enabled
148140
Route::post('/security/verify2fa', function () {
149141
return redirect()->route('home');

0 commit comments

Comments
 (0)