vendor/shopware/storefront/Controller/AuthController.php line 92

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Customer\Exception\BadCredentialsException;
  4. use Shopware\Core\Checkout\Customer\Exception\CustomerAuthThrottledException;
  5. use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundByHashException;
  6. use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundException;
  7. use Shopware\Core\Checkout\Customer\Exception\CustomerOptinNotCompletedException;
  8. use Shopware\Core\Checkout\Customer\Exception\CustomerRecoveryHashExpiredException;
  9. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLoginRoute;
  10. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
  11. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractResetPasswordRoute;
  12. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractSendPasswordRecoveryMailRoute;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  14. use Shopware\Core\Framework\Log\Package;
  15. use Shopware\Core\Framework\RateLimiter\Exception\RateLimitExceededException;
  16. use Shopware\Core\Framework\Routing\RoutingException;
  17. use Shopware\Core\Framework\Validation\DataBag\DataBag;
  18. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  19. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  20. use Shopware\Core\PlatformRequest;
  21. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
  22. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
  23. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  24. use Shopware\Storefront\Checkout\Cart\SalesChannel\StorefrontCartFacade;
  25. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  26. use Shopware\Storefront\Page\Account\Login\AccountGuestLoginPageLoadedHook;
  27. use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoadedHook;
  28. use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader;
  29. use Shopware\Storefront\Page\Account\RecoverPassword\AccountRecoverPasswordPageLoadedHook;
  30. use Shopware\Storefront\Page\Account\RecoverPassword\AccountRecoverPasswordPageLoader;
  31. use Symfony\Component\HttpFoundation\Request;
  32. use Symfony\Component\HttpFoundation\Response;
  33. use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
  34. use Symfony\Component\Routing\Annotation\Route;
  35. /**
  36.  * @internal
  37.  * Do not use direct or indirect repository calls in a controller. Always use a store-api route to get or put data
  38.  */
  39. #[Route(defaults: ['_routeScope' => ['storefront']])]
  40. #[Package('storefront')]
  41. class AuthController extends StorefrontController
  42. {
  43.     /**
  44.      * @internal
  45.      */
  46.     public function __construct(
  47.         private readonly AccountLoginPageLoader $loginPageLoader,
  48.         private readonly AbstractSendPasswordRecoveryMailRoute $sendPasswordRecoveryMailRoute,
  49.         private readonly AbstractResetPasswordRoute $resetPasswordRoute,
  50.         private readonly AbstractLoginRoute $loginRoute,
  51.         private readonly AbstractLogoutRoute $logoutRoute,
  52.         private readonly StorefrontCartFacade $cartFacade,
  53.         private readonly AccountRecoverPasswordPageLoader $recoverPasswordPageLoader,
  54.         private readonly SalesChannelContextServiceInterface $salesChannelContextService
  55.     ) {
  56.     }
  57.     #[Route(path'/account/login'name'frontend.account.login.page'defaults: ['_noStore' => true], methods: ['GET'])]
  58.     public function loginPage(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  59.     {
  60.         /** @var string $redirect */
  61.         $redirect $request->get('redirectTo''frontend.account.home.page');
  62.         $customer $context->getCustomer();
  63.         if ($customer !== null && $customer->getGuest() === false) {
  64.             $request->request->set('redirectTo'$redirect);
  65.             return $this->createActionResponse($request);
  66.         }
  67.         $page $this->loginPageLoader->load($request$context);
  68.         $this->hook(new AccountLoginPageLoadedHook($page$context));
  69.         return $this->renderStorefront('@Storefront/storefront/page/account/register/index.html.twig', [
  70.             'redirectTo' => $redirect,
  71.             'redirectParameters' => $request->get('redirectParameters'json_encode([])),
  72.             'errorRoute' => $request->attributes->get('_route'),
  73.             'page' => $page,
  74.             'loginError' => (bool) $request->get('loginError'),
  75.             'waitTime' => $request->get('waitTime'),
  76.             'errorSnippet' => $request->get('errorSnippet'),
  77.             'data' => $data,
  78.         ]);
  79.     }
  80.     #[Route(path'/account/guest/login'name'frontend.account.guest.login.page'defaults: ['_noStore' => true], methods: ['GET'])]
  81.     public function guestLoginPage(Request $requestSalesChannelContext $context): Response
  82.     {
  83.         /** @var string $redirect */
  84.         $redirect $request->get('redirectTo''frontend.account.home.page');
  85.         $customer $context->getCustomer();
  86.         if ($customer !== null) {
  87.             $request->request->set('redirectTo'$redirect);
  88.             return $this->createActionResponse($request);
  89.         }
  90.         $waitTime = (int) $request->get('waitTime');
  91.         if ($waitTime) {
  92.             $this->addFlash(self::INFO$this->trans('account.loginThrottled', ['%seconds%' => $waitTime]));
  93.         }
  94.         if ((bool) $request->get('loginError')) {
  95.             $this->addFlash(self::DANGER$this->trans('account.orderGuestLoginWrongCredentials'));
  96.         }
  97.         $page $this->loginPageLoader->load($request$context);
  98.         $this->hook(new AccountGuestLoginPageLoadedHook($page$context));
  99.         return $this->renderStorefront('@Storefront/storefront/page/account/guest-auth.html.twig', [
  100.             'redirectTo' => $redirect,
  101.             'redirectParameters' => $request->get('redirectParameters'json_encode([])),
  102.             'page' => $page,
  103.         ]);
  104.     }
  105.     #[Route(path'/account/logout'name'frontend.account.logout.page'methods: ['GET'])]
  106.     public function logout(Request $requestSalesChannelContext $contextRequestDataBag $dataBag): Response
  107.     {
  108.         if ($context->getCustomer() === null) {
  109.             return $this->redirectToRoute('frontend.account.login.page');
  110.         }
  111.         try {
  112.             $this->logoutRoute->logout($context$dataBag);
  113.             $this->addFlash(self::SUCCESS$this->trans('account.logoutSucceeded'));
  114.             $parameters = [];
  115.         } catch (ConstraintViolationException $formViolations) {
  116.             $parameters = ['formViolations' => $formViolations];
  117.         }
  118.         return $this->redirectToRoute('frontend.account.login.page'$parameters);
  119.     }
  120.     #[Route(path'/account/login'name'frontend.account.login'defaults: ['XmlHttpRequest' => true], methods: ['POST'])]
  121.     public function login(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  122.     {
  123.         $customer $context->getCustomer();
  124.         if ($customer !== null && $customer->getGuest() === false) {
  125.             return $this->createActionResponse($request);
  126.         }
  127.         try {
  128.             $token $this->loginRoute->login($data$context)->getToken();
  129.             $cartBeforeNewContext $this->cartFacade->get($token$context);
  130.             $newContext $this->salesChannelContextService->get(
  131.                 new SalesChannelContextServiceParameters(
  132.                     $context->getSalesChannelId(),
  133.                     $token,
  134.                     $context->getLanguageIdChain()[0],
  135.                     $context->getCurrencyId(),
  136.                     $context->getDomainId(),
  137.                     $context->getContext()
  138.                 )
  139.             );
  140.             // Update the sales channel context for CacheResponseSubscriber
  141.             $request->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT$newContext);
  142.             if (!empty($token)) {
  143.                 $this->addCartErrors($cartBeforeNewContext);
  144.                 return $this->createActionResponse($request);
  145.             }
  146.         } catch (BadCredentialsException|UnauthorizedHttpException|CustomerOptinNotCompletedException|CustomerAuthThrottledException $e) {
  147.             if ($e instanceof CustomerOptinNotCompletedException) {
  148.                 $errorSnippet $e->getSnippetKey();
  149.             }
  150.             if ($e instanceof CustomerAuthThrottledException) {
  151.                 $waitTime $e->getWaitTime();
  152.             }
  153.         }
  154.         $data->set('password'null);
  155.         return $this->forwardToRoute(
  156.             'frontend.account.login.page',
  157.             [
  158.                 'loginError' => true,
  159.                 'errorSnippet' => $errorSnippet ?? null,
  160.                 'waitTime' => $waitTime ?? null,
  161.             ]
  162.         );
  163.     }
  164.     #[Route(path'/account/recover'name'frontend.account.recover.page'methods: ['GET'])]
  165.     public function recoverAccountForm(Request $requestSalesChannelContext $context): Response
  166.     {
  167.         $page $this->loginPageLoader->load($request$context);
  168.         return $this->renderStorefront('@Storefront/storefront/page/account/profile/recover-password.html.twig', [
  169.             'page' => $page,
  170.         ]);
  171.     }
  172.     #[Route(path'/account/recover'name'frontend.account.recover.request'methods: ['POST'])]
  173.     public function generateAccountRecovery(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  174.     {
  175.         try {
  176.             $mailData $data->get('email');
  177.             if (!$mailData instanceof DataBag) {
  178.                 throw RoutingException::invalidRequestParameter('email');
  179.             }
  180.             $mailData->set('storefrontUrl'$request->attributes->get(RequestTransformer::STOREFRONT_URL));
  181.             $this->sendPasswordRecoveryMailRoute->sendRecoveryMail(
  182.                 $mailData->toRequestDataBag(),
  183.                 $context,
  184.                 false
  185.             );
  186.             $this->addFlash(self::SUCCESS$this->trans('account.recoveryMailSend'));
  187.         } catch (CustomerNotFoundException $e) {
  188.             $this->addFlash(self::SUCCESS$this->trans('account.recoveryMailSend'));
  189.         } catch (InconsistentCriteriaIdsException $e) {
  190.             $this->addFlash(self::DANGER$this->trans('error.message-default'));
  191.         } catch (RateLimitExceededException $e) {
  192.             $this->addFlash(self::INFO$this->trans('error.rateLimitExceeded', ['%seconds%' => $e->getWaitTime()]));
  193.         }
  194.         return $this->redirectToRoute('frontend.account.recover.page');
  195.     }
  196.     #[Route(path'/account/recover/password'name'frontend.account.recover.password.page'methods: ['GET'])]
  197.     public function resetPasswordForm(Request $requestSalesChannelContext $context): Response
  198.     {
  199.         /** @var ?string $hash */
  200.         $hash $request->get('hash');
  201.         if (!$hash || !\is_string($hash)) {
  202.             $this->addFlash(self::DANGER$this->trans('account.passwordHashNotFound'));
  203.             return $this->redirectToRoute('frontend.account.recover.request');
  204.         }
  205.         try {
  206.             $page $this->recoverPasswordPageLoader->load($request$context$hash);
  207.         } catch (ConstraintViolationException|CustomerNotFoundByHashException) {
  208.             $this->addFlash(self::DANGER$this->trans('account.passwordHashNotFound'));
  209.             return $this->redirectToRoute('frontend.account.recover.request');
  210.         }
  211.         $this->hook(new AccountRecoverPasswordPageLoadedHook($page$context));
  212.         if ($page->getHash() === null || $page->isHashExpired()) {
  213.             $this->addFlash(self::DANGER$this->trans('account.passwordHashNotFound'));
  214.             return $this->redirectToRoute('frontend.account.recover.request');
  215.         }
  216.         return $this->renderStorefront('@Storefront/storefront/page/account/profile/reset-password.html.twig', [
  217.             'page' => $page,
  218.             'formViolations' => $request->get('formViolations'),
  219.         ]);
  220.     }
  221.     #[Route(path'/account/recover/password'name'frontend.account.recover.password.reset'methods: ['POST'])]
  222.     public function resetPassword(RequestDataBag $dataSalesChannelContext $context): Response
  223.     {
  224.         $passwordData $data->get('password');
  225.         if (!$passwordData instanceof DataBag) {
  226.             throw RoutingException::invalidRequestParameter('password');
  227.         }
  228.         $hash $passwordData->get('hash');
  229.         try {
  230.             $this->resetPasswordRoute->resetPassword($passwordData->toRequestDataBag(), $context);
  231.             $this->addFlash(self::SUCCESS$this->trans('account.passwordChangeSuccess'));
  232.         } catch (ConstraintViolationException $formViolations) {
  233.             if ($formViolations->getViolations('newPassword')->count() === 1) {
  234.                 $this->addFlash(self::DANGER$this->trans('account.passwordNotIdentical'));
  235.             } else {
  236.                 $this->addFlash(self::DANGER$this->trans('account.passwordChangeNoSuccess'));
  237.             }
  238.             return $this->forwardToRoute(
  239.                 'frontend.account.recover.password.page',
  240.                 ['hash' => $hash'formViolations' => $formViolations'passwordFormViolation' => true]
  241.             );
  242.         } catch (CustomerNotFoundByHashException) {
  243.             $this->addFlash(self::DANGER$this->trans('account.passwordChangeNoSuccess'));
  244.             return $this->forwardToRoute('frontend.account.recover.request');
  245.         } catch (CustomerRecoveryHashExpiredException) {
  246.             $this->addFlash(self::DANGER$this->trans('account.passwordHashExpired'));
  247.             return $this->forwardToRoute('frontend.account.recover.request');
  248.         }
  249.         return $this->redirectToRoute('frontend.account.profile.page');
  250.     }
  251. }