1515use App \Http \Controllers \APICRUDController ;
1616use App \Http \Controllers \Traits \RequestProcessor ;
1717use App \Http \Controllers \UserValidationRulesFactory ;
18+ use App \libs \Auth \Models \TwoFactorAuditLog ;
1819use App \ModelSerializers \SerializerRegistry ;
20+ use App \Services \Auth \IRecoveryCodeService ;
21+ use App \Services \Auth \ITwoFactorAuditService ;
1922use Auth \Repositories \IUserRepository ;
2023use Auth \User ;
2124use Exception ;
2225use Illuminate \Http \Request as LaravelRequest ;
2326use Illuminate \Support \Facades \Auth ;
2427use Illuminate \Support \Facades \Log ;
2528use Illuminate \Support \Facades \Request ;
29+ use Illuminate \Support \Facades \Validator ;
2630use models \exceptions \EntityNotFoundException ;
2731use models \exceptions \ValidationException ;
2832use OAuth2 \Services \ITokenService ;
2933use OpenId \Services \IUserService ;
34+ use Utils \Db \ITransactionService ;
35+ use Utils \IPHelper ;
3036use Utils \Services \ILogService ;
3137
3238/**
@@ -43,23 +49,47 @@ final class UserApiController extends APICRUDController
4349 */
4450 private $ token_service ;
4551
52+ /**
53+ * @var IRecoveryCodeService
54+ */
55+ private $ recovery_code_service ;
56+
57+ /**
58+ * @var ITransactionService
59+ */
60+ private $ tx_service ;
61+
62+ /**
63+ * @var ITwoFactorAuditService
64+ */
65+ private $ two_factor_audit_service ;
66+
4667 /**
4768 * UserApiController constructor.
4869 * @param IUserRepository $user_repository
4970 * @param ILogService $log_service
5071 * @param IUserService $user_service
5172 * @param ITokenService $token_service
73+ * @param IRecoveryCodeService $recovery_code_service
74+ * @param ITransactionService $tx_service
75+ * @param ITwoFactorAuditService $two_factor_audit_service
5276 */
5377 public function __construct
5478 (
5579 IUserRepository $ user_repository ,
5680 ILogService $ log_service ,
5781 IUserService $ user_service ,
58- ITokenService $ token_service
82+ ITokenService $ token_service ,
83+ IRecoveryCodeService $ recovery_code_service ,
84+ ITransactionService $ tx_service ,
85+ ITwoFactorAuditService $ two_factor_audit_service
5986 )
6087 {
6188 parent ::__construct ($ user_repository , $ user_service , $ log_service );
6289 $ this ->token_service = $ token_service ;
90+ $ this ->recovery_code_service = $ recovery_code_service ;
91+ $ this ->tx_service = $ tx_service ;
92+ $ this ->two_factor_audit_service = $ two_factor_audit_service ;
6393 }
6494
6595 /**
@@ -247,6 +277,76 @@ public function updateMe()
247277 return $ this ->update (Auth::user ()->getId ());
248278 }
249279
280+ /**
281+ * Enables a 2FA method for the current user and generates the first batch of
282+ * recovery codes for them. Plaintext codes are returned once in the response
283+ * and never persisted.
284+ *
285+ * @return \Illuminate\Http\JsonResponse|mixed
286+ */
287+ public function enableTwoFactor ()
288+ {
289+ if (!Auth::check ())
290+ return $ this ->error403 ();
291+
292+ return $ this ->processRequest (function () {
293+ $ data = Request::all ();
294+ $ validator = Validator::make ($ data , [
295+ 'method ' => 'required|string|in: ' . implode (', ' , User::ValidMFAMethods),
296+ ]);
297+
298+ if (!$ validator ->passes ()) {
299+ return $ this ->error412 ($ validator ->getMessageBag ()->getMessages ());
300+ }
301+
302+ $ user = Auth::user ();
303+ $ method = $ data ['method ' ];
304+
305+ $ codes = $ this ->tx_service ->transaction (function () use ($ user , $ method ) {
306+ $ user ->enable2FA ($ method );
307+ $ this ->repository ->add ($ user , false );
308+
309+ return $ this ->recovery_code_service ->generateRecoveryCodes ($ user );
310+ });
311+
312+ $ this ->two_factor_audit_service ->log (
313+ $ user ,
314+ TwoFactorAuditLog::EventEnrollmentChanged,
315+ $ method ,
316+ IPHelper::getUserIp ()
317+ );
318+
319+ return $ this ->ok (['recovery_codes ' => $ codes ]);
320+ });
321+ }
322+
323+ /**
324+ * Invalidates the current user's recovery codes and generates a fresh batch.
325+ * Plaintext codes are returned once in the response and never persisted.
326+ *
327+ * @return \Illuminate\Http\JsonResponse|mixed
328+ */
329+ public function regenerateRecoveryCodes ()
330+ {
331+ if (!Auth::check ())
332+ return $ this ->error403 ();
333+
334+ return $ this ->processRequest (function () {
335+ $ data = Request::all ();
336+ $ validator = Validator::make ($ data , [
337+ 'current_password ' => 'required|string ' ,
338+ ]);
339+
340+ if (!$ validator ->passes ()) {
341+ return $ this ->error412 ($ validator ->getMessageBag ()->getMessages ());
342+ }
343+
344+ $ codes = $ this ->recovery_code_service ->regenerateRecoveryCodes (Auth::user (), $ data ['current_password ' ]);
345+
346+ return $ this ->ok (['recovery_codes ' => $ codes ]);
347+ });
348+ }
349+
250350 public function revokeAllMyTokens ()
251351 {
252352 if (!Auth::check ())
0 commit comments