Passed
Push — master ( bf0d13...546dd7 )
by Darko
09:14
created

PasswordSecurityController::profileDisable2fa()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 17
rs 9.9666
cc 3
nc 3
nop 1
1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Http\Requests\Disable2faPasswordSecurityRequest;
6
use App\Models\PasswordSecurity;
7
use Illuminate\Contracts\View\Factory;
8
use Illuminate\Contracts\View\View;
9
use Illuminate\Foundation\Application;
10
use Illuminate\Http\RedirectResponse;
11
use Illuminate\Http\Request;
12
use Illuminate\Support\Facades\Auth;
13
use Illuminate\Support\Facades\Hash;
14
15
class PasswordSecurityController extends Controller
16
{
17
    public function show2faForm(Request $request): Application|View|Factory|\Illuminate\Contracts\Foundation\Application
18
    {
19
        $user = $request->user();
20
21
        $google2fa_url = '';
22
        if ($user->passwordSecurity()->exists()) {
23
            $google2fa_url = \Google2FA::getQRCodeInline(
0 ignored issues
show
Bug introduced by
The type Google2FA was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
                config('app.name'),
25
                $user->email,
26
                $user->passwordSecurity->google2fa_secret
27
            );
28
        }
29
        $data = [
30
            'user' => $user,
31
            'google2fa_url' => $google2fa_url,
32
        ];
33
34
        return view('auth.2fa')->with('data', $data);
35
    }
36
37
    /**
38
     * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
39
     * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
40
     * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
41
     */
42
    public function generate2faSecret(Request $request): RedirectResponse
43
    {
44
        $user = $request->user();
45
46
        // Add the secret key to the registration data
47
        PasswordSecurity::create(
48
            [
49
                'user_id' => $user->id,
50
                'google2fa_enable' => 0,
51
                'google2fa_secret' => \Google2FA::generateSecretKey(),
52
            ]
53
        );
54
55
        return redirect()->to('2fa')->with('success', 'Secret Key is generated, Please verify Code to Enable 2FA');
56
    }
57
58
    /**
59
     * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
60
     * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
61
     * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
62
     */
63
    public function enable2fa(Request $request): RedirectResponse
64
    {
65
        $user = $request->user();
66
        $secret = $request->input('verify-code');
67
        $valid = \Google2FA::verifyKey($user->passwordSecurity->google2fa_secret, $secret);
68
        if ($valid) {
69
            $user->passwordSecurity->google2fa_enable = 1;
70
            $user->passwordSecurity->save();
71
72
            // Check if we should redirect to profile page
73
            if ($request->has('redirect_to_profile')) {
74
                return redirect()->to('profileedit#security')->with('success_2fa', '2FA is Enabled Successfully.');
75
            }
76
77
            return redirect()->to('2fa')->with('success', '2FA is Enabled Successfully.');
78
        }
79
80
        // Check if we should redirect to profile page on failure as well
81
        if ($request->has('redirect_to_profile')) {
82
            return redirect()->to('profileedit#security')->with('error_2fa', 'Invalid Verification Code, Please try again.');
83
        }
84
85
        return redirect()->to('2fa')->with('error', 'Invalid Verification Code, Please try again.');
86
    }
87
88
    public function disable2fa(Disable2faPasswordSecurityRequest $request): \Illuminate\Routing\Redirector|RedirectResponse|\Illuminate\Contracts\Foundation\Application
89
    {
90
        if (! (Hash::check($request->get('current-password'), $request->user()->password))) {
91
            // Password doesn't match
92
            if ($request->has('redirect_to_profile') || $request->has('from_profile')) {
93
                return redirect()->to('profileedit#security')->with('error_2fa', 'Your password does not match with your account password. Please try again.');
94
            }
95
96
            return redirect()->back()->with('error', 'Your password does not match with your account password. Please try again.');
97
        }
98
99
        $validatedData = $request->validated();
0 ignored issues
show
Unused Code introduced by
The assignment to $validatedData is dead and can be removed.
Loading history...
100
        $user = $request->user();
101
        $user->passwordSecurity->google2fa_enable = 0;
102
        $user->passwordSecurity->save();
103
104
        // Check if this request is from the profile edit page
105
        if ($request->has('redirect_to_profile') || $request->has('from_profile')) {
106
            return redirect()->to('profileedit#security')->with('success_2fa', '2FA is now Disabled.');
107
        }
108
109
        return redirect()->to('2fa')->with('success', '2FA is now Disabled.');
110
    }
111
112
    /**
113
     * Verify the 2FA code provided by the user.
114
     */
115
    public function verify2fa(Request $request): RedirectResponse
116
    {
117
        $request->validate([
118
            'one_time_password' => 'required|numeric',
119
        ]);
120
121
        // Get the user ID from session
122
        if (! $request->session()->has('2fa:user:id')) {
123
            return redirect()->route('login')
124
                ->with('message', 'The two-factor authentication session has expired. Please login again.')
125
                ->with('message_type', 'danger');
126
        }
127
128
        $userId = $request->session()->get('2fa:user:id');
129
        $user = \App\Models\User::find($userId);
130
131
        if (! $user || ! $user->passwordSecurity) {
132
            $request->session()->forget('2fa:user:id');
133
134
            return redirect()->route('login')
135
                ->with('message', 'User not found or 2FA not configured. Please login again.')
136
                ->with('message_type', 'danger');
137
        }
138
139
        // Verify the OTP code
140
        $valid = \Google2FA::verifyKey(
141
            $user->passwordSecurity->google2fa_secret,
142
            $request->input('one_time_password')
143
        );
144
145
        if (! $valid) {
146
            return redirect()->route('2fa.verify')
147
                ->with('message', 'Invalid authentication code. Please try again.')
148
                ->with('message_type', 'danger');
149
        }
150
151
        // Log the user back in
152
        Auth::login($user);
153
154
        // Mark the user as having passed 2FA
155
        session([config('google2fa.session_var') => true]);
156
157
        // Store the timestamp for determining how long the 2FA session is valid
158
        session([config('google2fa.session_var').'.auth.passed_at' => time()]);
159
160
        // Clean up the temporary session variable
161
        $request->session()->forget('2fa:user:id');
162
163
        // Determine where to redirect after successful verification
164
        $redirectUrl = $request->session()->pull('url.intended', '/');
165
166
        return redirect()->to($redirectUrl)
167
            ->with('message', 'Two-factor authentication verified successfully.')
168
            ->with('message_type', 'success');
169
    }
170
171
    /**
172
     * Display the 2FA verification form for a user who has already authenticated with username/password
173
     * but needs to enter their 2FA code.
174
     */
175
    public function getVerify2fa(Request $request)
176
    {
177
        // Check if user ID is stored in the session
178
        if (! $request->session()->has('2fa:user:id')) {
179
            return redirect()->route('login')
180
                ->withErrors(['msg' => 'The two-factor authentication session has expired. Please login again.']);
181
        }
182
183
        // Get the user ID from session
184
        $userId = $request->session()->get('2fa:user:id');
185
186
        // Get the user
187
        $user = \App\Models\User::find($userId);
188
        if (! $user) {
189
            $request->session()->forget('2fa:user:id');
190
191
            return redirect()->route('login')
192
                ->withErrors(['msg' => 'User not found. Please login again.']);
193
        }
194
195
        $theme = 'Gentele';
196
        $meta_title = 'Two Factor Authentication';
197
        $meta_keywords = 'Two Factor Authentication, 2FA';
198
        $meta_description = 'Two Factor Authentication Verification';
199
200
        app('smarty.view')->assign(compact('meta_title', 'meta_keywords', 'meta_description', 'user'));
201
202
        return app('smarty.view')->display($theme.'/2fa_verify.tpl');
203
    }
204
205
    /**
206
     * Handle disabling 2FA directly from profile page to avoid form conflicts.
207
     * This route is specifically for the profile page 2FA section.
208
     */
209
    public function profileDisable2fa(Request $request): RedirectResponse
210
    {
211
        $request->validate([
212
            'current-password' => 'required',
213
        ]);
214
215
        if (! (Hash::check($request->get('current-password'), $request->user()->password))) {
216
            return redirect()->to('profileedit#security')->with('error_2fa', 'Your password does not match with your account password. Please try again.');
217
        }
218
219
        $user = $request->user();
220
        if ($user->passwordSecurity) {
221
            $user->passwordSecurity->google2fa_enable = 0;
222
            $user->passwordSecurity->save();
223
        }
224
225
        return redirect()->to('profileedit#security')->with('success_2fa', '2FA is now Disabled.');
226
    }
227
228
    /**
229
     * Show the 2FA enable form on a dedicated page
230
     */
231
    public function showEnable2faForm(Request $request): Application|View|Factory|\Illuminate\Contracts\Foundation\Application
232
    {
233
        $user = $request->user();
234
        $success = $request->session()->get('success');
235
        $error = $request->session()->get('error');
236
237
        $google2fa_url = '';
238
        if ($user->passwordSecurity()->exists()) {
239
            $google2fa_url = \Google2FA::getQRCodeInline(
240
                config('app.name'),
241
                $user->email,
242
                $user->passwordSecurity->google2fa_secret
243
            );
244
        }
245
246
        return view('themes.Gentele.2fa_enable', compact('user', 'google2fa_url', 'success', 'error'));
247
    }
248
249
    /**
250
     * Show the 2FA disable form on a dedicated page
251
     */
252
    public function showDisable2faForm(Request $request): Application|View|Factory|\Illuminate\Contracts\Foundation\Application
253
    {
254
        $user = $request->user();
255
        $success = $request->session()->get('success');
256
        $error = $request->session()->get('error');
257
258
        return view('themes.Gentele.2fa_disable', compact('user', 'success', 'error'));
259
    }
260
}
261