Passed
Push — master ( ec8b22...b724fa )
by Darko
08:07
created

TrustedDevice2FAMiddleware   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 10
eloc 43
c 1
b 0
f 0
dl 0
loc 79
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
B handle() 0 74 10
1
<?php
2
3
namespace App\Http\Middleware;
4
5
use Closure;
6
use Illuminate\Http\Request;
7
use Illuminate\Support\Facades\Log;
8
9
class TrustedDevice2FAMiddleware
10
{
11
    /**
12
     * Handle an incoming request.
13
     */
14
    public function handle(Request $request, Closure $next): mixed
15
    {
16
        // Check for trusted device cookie on incoming request
17
        $trustedCookie = $request->cookie('2fa_trusted_device');
18
19
        if ($trustedCookie && auth()->check()) {
20
            try {
21
                $cookieData = json_decode($trustedCookie, true);
0 ignored issues
show
Bug introduced by
It seems like $trustedCookie can also be of type array; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

21
                $cookieData = json_decode(/** @scrutinizer ignore-type */ $trustedCookie, true);
Loading history...
22
23
                // If cookie data is valid and user matches
24
                if (json_last_error() === JSON_ERROR_NONE &&
25
                    isset($cookieData['user_id'], $cookieData['token'], $cookieData['expires_at']) &&
26
                    (int) $cookieData['user_id'] === (int) auth()->id() &&
27
                    time() <= $cookieData['expires_at']) {
28
29
                    // Mark this user's session as having passed 2FA
30
                    session([config('google2fa.session_var') => true]);
31
                    session([config('google2fa.session_var').'.auth.passed_at' => time()]);
32
                }
33
            } catch (\Exception $e) {
34
                Log::error('TrustedDevice2FAMiddleware - Error processing cookie', [
35
                    'error' => $e->getMessage(),
36
                ]);
37
            }
38
        }
39
40
        // Process the request
41
        $response = $next($request);
42
43
        // Check if we need to set a trusted device cookie
44
        if ($request->session()->has('2fa_trusted_device_pending')) {
45
            $cookieData = $request->session()->pull('2fa_trusted_device_pending');
46
47
            // Ensure cookie data is properly formatted
48
            $cookieValue = json_encode($cookieData, JSON_UNESCAPED_SLASHES);
49
50
            try {
51
                // Create a cookie instance with proper settings for persistence
52
                $cookie = cookie(
53
                    '2fa_trusted_device',      // name
54
                    $cookieValue,              // value
55
                    60 * 24 * 30,              // minutes (30 days)
56
                    '/',                       // path
57
                    null,                      // domain (null = current domain)
58
                    null,                      // secure (auto)
59
                    false,                     // httpOnly - allow JS access
60
                    false,                     // raw
61
                    'lax'                      // sameSite
62
                );
63
64
                // Add cookie to the response
65
                $response->headers->setCookie($cookie);
66
67
                // Backup approach - also set directly in PHP
68
                $expiry = time() + (60 * 60 * 24 * 30); // 30 days
69
                @setcookie('2fa_trusted_device', $cookieValue, [
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for setcookie(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

69
                /** @scrutinizer ignore-unhandled */ @setcookie('2fa_trusted_device', $cookieValue, [

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
70
                    'expires' => $expiry,
71
                    'path' => '/',
72
                    'domain' => '',
73
                    'secure' => $request->secure(),
74
                    'httponly' => false,
75
                    'samesite' => 'Lax',
76
                ]);
77
78
                // Keep in session for backup access
79
                $request->session()->put('2fa_trusted_device_value', $cookieValue);
80
            } catch (\Exception $e) {
81
                Log::error('TrustedDevice2FAMiddleware - Error setting cookie', [
82
                    'error' => $e->getMessage(),
83
                ]);
84
            }
85
        }
86
87
        return $response;
88
    }
89
}
90