55use CodedMonkey \Dirigent \Doctrine \Entity \User ;
66use CodedMonkey \Dirigent \Doctrine \Repository \UserRepository ;
77use CodedMonkey \Dirigent \Form \AccountFormType ;
8+ use CodedMonkey \Dirigent \Form \AccountMfaFormType ;
89use CodedMonkey \Dirigent \Form \ChangePasswordFormType ;
910use EasyCorp \Bundle \EasyAdminBundle \Router \AdminUrlGenerator ;
11+ use Scheb \TwoFactorBundle \Security \TwoFactor \Provider \Totp \TotpAuthenticatorInterface ;
1012use Symfony \Bundle \FrameworkBundle \Controller \AbstractController ;
1113use Symfony \Component \Form \FormError ;
1214use Symfony \Component \HttpFoundation \Request ;
@@ -28,6 +30,7 @@ public static function getSubscribedServices(): array
2830 public function __construct (
2931 private readonly UserRepository $ userRepository ,
3032 private readonly UserPasswordHasherInterface $ passwordHasher ,
33+ private readonly TotpAuthenticatorInterface $ totpAuthenticator ,
3134 ) {
3235 }
3336
@@ -36,8 +39,6 @@ public function __construct(
3639 public function account (Request $ request , #[CurrentUser] User $ user ): Response
3740 {
3841 $ accountForm = $ this ->createForm (AccountFormType::class, $ user );
39- $ passwordForm = $ this ->createForm (ChangePasswordFormType::class);
40-
4142 $ accountForm ->handleRequest ($ request );
4243
4344 if ($ accountForm ->isSubmitted () && $ accountForm ->isValid ()) {
@@ -50,6 +51,7 @@ public function account(Request $request, #[CurrentUser] User $user): Response
5051 return $ this ->redirect ($ url );
5152 }
5253
54+ $ passwordForm = $ this ->createForm (ChangePasswordFormType::class);
5355 $ passwordForm ->handleRequest ($ request );
5456
5557 if ($ passwordForm ->isSubmitted ()) {
@@ -77,4 +79,57 @@ public function account(Request $request, #[CurrentUser] User $user): Response
7779 'passwordForm ' => $ passwordForm ,
7880 ]);
7981 }
82+
83+ #[Route('/dashboard/account/mfa ' , name: 'dashboard_account_mfa ' )]
84+ #[IsGranted('ROLE_USER ' )]
85+ public function mfa (Request $ request , #[CurrentUser] User $ user ): Response
86+ {
87+ if (!$ user ->isTotpAuthenticationEnabled ()) {
88+ $ session = $ request ->getSession ();
89+
90+ if (null === $ totpSecret = $ session ->get ('totp_secret ' )) {
91+ $ totpSecret = $ this ->totpAuthenticator ->generateSecret ();
92+
93+ $ session ->set ('totp_secret ' , $ totpSecret );
94+ }
95+
96+ $ form = $ this ->createForm (AccountMfaFormType::class);
97+ $ form ->handleRequest ($ request );
98+
99+ if ($ form ->isSubmitted ()) {
100+ $ currentPassword = $ form ->get ('currentPassword ' )->getData ();
101+
102+ if (!$ this ->passwordHasher ->isPasswordValid ($ user , $ currentPassword )) {
103+ $ form ->get ('currentPassword ' )->addError (new FormError ('Your current password is incorrect. ' ));
104+ }
105+
106+ $ user ->setTotpSecret ($ totpSecret );
107+
108+ $ totpCode = $ form ->get ('totpCode ' )->getData ();
109+
110+ if (!$ this ->totpAuthenticator ->checkCode ($ user , $ totpCode )) {
111+ $ user ->setTotpSecret (null );
112+
113+ $ form ->get ('totpCode ' )->addError (new FormError ('The verification code is incorrect. ' ));
114+ }
115+
116+ if ($ form ->isValid ()) {
117+ $ this ->userRepository ->save ($ user , true );
118+
119+ $ this ->addFlash ('success ' , 'Multi-factor authentication was successfully enabled. ' );
120+
121+ $ session ->remove ('totp_secret ' );
122+
123+ $ url = $ this ->container ->get (AdminUrlGenerator::class)->setRoute ('dashboard_account ' )->generateUrl ();
124+
125+ return $ this ->redirect ($ url );
126+ }
127+ }
128+
129+ return $ this ->render ('dashboard/account/mfa.html.twig ' , [
130+ 'form ' => $ form ,
131+ 'totpSecret ' => $ totpSecret ,
132+ ]);
133+ }
134+ }
80135}
0 commit comments