Skip to content

Commit ab5d708

Browse files
authored
Merge pull request #1030 from owlchester/security-2fa-livewire
Security: 2fa migrated to livewire
2 parents 951b10b + d82d8b3 commit ab5d708

File tree

7 files changed

+168
-72
lines changed

7 files changed

+168
-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

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

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)