1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace Admin \Admin \Handler \Account ;
6+
7+ use Admin \Admin \Form \TotpForm ;
8+ use Dot \DependencyInjection \Attribute \Inject ;
9+ use Dot \FlashMessenger \FlashMessengerInterface ;
10+ use Dot \Totp \Totp ;
11+ use Laminas \Authentication \AuthenticationService ;
12+ use Laminas \Authentication \Exception \ExceptionInterface ;
13+ use Laminas \Diactoros \Response \EmptyResponse ;
14+ use Laminas \Diactoros \Response \HtmlResponse ;
15+ use Mezzio \Router \RouterInterface ;
16+ use Mezzio \Template \TemplateRendererInterface ;
17+ use Psr \Http \Message \ResponseInterface ;
18+ use Psr \Http \Message \ServerRequestInterface ;
19+ use Psr \Http \Server \RequestHandlerInterface ;
20+ use Random \RandomException ;
21+
22+ use function time ;
23+
24+ class GetEnableTotpFormHandler implements RequestHandlerInterface
25+ {
26+ private const int SECRET_MAX_AGE = 600 ;
27+
28+ /**
29+ * @param array{label: string, issuer: string} $provisioningUri
30+ */
31+ #[Inject(
32+ Totp::class,
33+ AuthenticationService::class,
34+ FlashMessengerInterface::class,
35+ TemplateRendererInterface::class,
36+ TotpForm::class,
37+ RouterInterface::class,
38+ 'config.dot_totp.provision_uri_config '
39+ )]
40+ public function __construct (
41+ protected Totp $ totpService ,
42+ protected AuthenticationService $ authenticationService ,
43+ protected FlashMessengerInterface $ messenger ,
44+ protected TemplateRendererInterface $ template ,
45+ protected TotpForm $ totpForm ,
46+ protected RouterInterface $ router ,
47+ protected array $ provisioningUri
48+ ) {
49+ }
50+
51+ /**
52+ * @throws ExceptionInterface
53+ * @throws RandomException
54+ */
55+ public function handle (ServerRequestInterface $ request ): ResponseInterface |EmptyResponse |HtmlResponse
56+ {
57+ $ storage = $ this ->authenticationService ->getStorage ()->read ();
58+
59+ if (
60+ empty ($ storage ->pendingSecret ) ||
61+ empty ($ storage ->secretTimestamp ) ||
62+ (time () - $ storage ->secretTimestamp ) > self ::SECRET_MAX_AGE
63+ ) {
64+ $ storage ->pendingSecret = $ this ->totpService ->generateSecretBase32 ();
65+ $ storage ->secretTimestamp = time ();
66+ $ this ->authenticationService ->getStorage ()->write ($ storage );
67+ }
68+
69+ $ uri = $ this ->totpService ->getProvisioningUri (
70+ $ storage ->getIdentity (),
71+ $ this ->provisioningUri ['issuer ' ],
72+ $ storage ->pendingSecret
73+ );
74+
75+ $ qrSvg = $ this ->totpService ->generateInlineSvgQr ($ uri );
76+ $ storage ->totp_verified = false ;
77+
78+ if (isset ($ storage ->recovery_auth ) && $ storage ->recovery_auth ) {
79+ $ this ->totpForm ->setAttribute ('title ' , 'Reconfigure Two-Factor Authentication ' );
80+ }
81+
82+ $ this ->totpForm ->setAttribute ('action ' , $ this ->router ->generateUri ('admin::enable-totp ' ));
83+
84+ return new HtmlResponse (
85+ $ this ->template ->render ('admin::validate-totp-form ' , [
86+ 'qrSvg ' => $ qrSvg ,
87+ 'cancelUrl ' => $ request ->getAttribute ('cancelUrl ' ),
88+ 'totpForm ' => $ this ->totpForm ->prepare (),
89+ 'error ' => null ,
90+ ])
91+ );
92+ }
93+ }
0 commit comments