4
4
5
5
use App \Model \Authentication \TwoFactor ;
6
6
use App \Service \Authentication \AccountService ;
7
+ use App \Software ;
7
8
use App \Table \Account \TwoFactorTable ;
9
+ use Endroid \QrCode \Builder \Builder ;
10
+ use Endroid \QrCode \ErrorCorrectionLevel \ErrorCorrectionLevelHigh ;
11
+ use Endroid \QrCode \RoundBlockSizeMode \RoundBlockSizeModeMargin ;
12
+ use Endroid \QrCode \Writer \SvgWriter ;
8
13
use OTPHP \TOTP ;
14
+ use ParagonIE \ConstantTime \Base32 ;
9
15
10
- class SecurityService
16
+ readonly class SecurityService
11
17
{
12
18
13
19
public function __construct (
14
- private readonly AccountService $ accountService ,
15
- private readonly TwoFactorTable $ twoFactorTable
20
+ private AccountService $ accountService ,
21
+ private TwoFactorTable $ twoFactorTable
16
22
)
17
23
{
18
24
}
@@ -26,9 +32,9 @@ public function add(TwoFactor $twoFactor, int $code): void
26
32
27
33
$ totp = $ this ->generateTOTPFromSecret ($ twoFactor ->getSecret ());
28
34
29
- if ($ totp -> verify ( $ code , null , 5 ) === FALSE )
35
+ if ($ this -> verifyTOTPBySecret ( $ twoFactor -> getSecret (), ( string ) $ code ) === FALSE )
30
36
{
31
- MESSAGES ->add ('danger ' , 'account-settings-security-two-factor-failed-code-invalid ' , $ totp -> now () );
37
+ MESSAGES ->add ('danger ' , 'account-settings-security-two-factor-failed-code-invalid ' );
32
38
return ;
33
39
}
34
40
@@ -62,7 +68,7 @@ public function verifyAccountTwoFactor(int $account, string $code): bool
62
68
public function verifyTOTPBySecret (string $ secret , string $ code ): bool
63
69
{
64
70
$ totp = $ this ->generateTOTPFromSecret ($ secret );
65
- return $ totp ->verify ($ code );
71
+ return $ totp ->verify ($ code, null , ( int ) $ _ENV [ ' MFA_TOTP_TIME_WINDOW ' ] ?? 5 );
66
72
}
67
73
68
74
public function getTwoFactorByAccountID (int $ account ): array |false
@@ -72,14 +78,33 @@ public function getTwoFactorByAccountID(int $account): array|false
72
78
73
79
public function generateTOTPSecret (): string
74
80
{
75
- return trim (\ ParagonIE \ ConstantTime \ Base32::encodeUpper (random_bytes ($ _ENV ['MFA_TOTP_GEN_BITS ' ] ?? 10 )), '= ' );
81
+ return trim (Base32::encodeUpper (random_bytes (isset ( $ _ENV ['MFA_TOTP_GEN_BITS ' ]) ? ( int ) $ _ENV [ ' MFA_TOTP_GEN_BITS ' ] : 10 )), '= ' );
76
82
}
77
83
78
- private function generateTOTPFromSecret (string $ secret ): TOTP
84
+ public function generateTOTPQrCodeBase64 (TOTP $ totp ): string
85
+ {
86
+
87
+ $ qrcode = Builder::create ()
88
+ ->writer (new SvgWriter ())
89
+ ->data ($ totp ->getProvisioningUri ())
90
+ ->size (300 )
91
+ ->errorCorrectionLevel (new ErrorCorrectionLevelHigh ())
92
+ ->margin (15 )
93
+ ->labelText ('2 Factor Auth ' )
94
+ ->roundBlockSizeMode (new RoundBlockSizeModeMargin ())
95
+ ->build ();
96
+
97
+ return $ qrcode ->getString ();
98
+
99
+ }
100
+
101
+ public function generateTOTPFromSecret (string $ secret , string $ label = '' ): TOTP
79
102
{
80
103
$ totp = TOTP ::createFromSecret ($ secret );
81
- $ totp ->setDigits (6 );
82
- $ totp ->setPeriod (30 );
104
+ $ totp ->setDigits ((int )$ _ENV ['MFA_TOTP_DIGITS ' ] ?? 6 );
105
+ $ totp ->setPeriod ((int )$ _ENV ['MFA_TOTP_PERIOD ' ] ?? 30 );
106
+ $ totp ->setIssuer ((string )$ _ENV ['MFA_ISSUER ' ] ?? 'Web-Toolkit ' );
107
+ $ totp ->setLabel (empty ($ label ) ? $ _ENV ['SOFTWARE_TITLE ' ] : $ label );
83
108
return $ totp ;
84
109
}
85
110
0 commit comments