src/Core/Checkout/Customer/Subscriber/CustomerTokenSubscriber.php line 40

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Customer\Subscriber;
  3. use Shopware\Core\Checkout\Customer\CustomerEvents;
  4. use Shopware\Core\Framework\DataAbstractionLayer\EntityWriteResult;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityDeletedEvent;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  7. use Shopware\Core\Framework\Log\Package;
  8. use Shopware\Core\PlatformRequest;
  9. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextPersister;
  10. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. use Symfony\Component\HttpFoundation\RequestStack;
  13. /**
  14.  * @internal
  15.  */
  16. #[Package('customer-order')]
  17. class CustomerTokenSubscriber implements EventSubscriberInterface
  18. {
  19.     /**
  20.      * @internal
  21.      */
  22.     public function __construct(private readonly SalesChannelContextPersister $contextPersister, private readonly RequestStack $requestStack)
  23.     {
  24.     }
  25.     /**
  26.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  27.      */
  28.     public static function getSubscribedEvents(): array
  29.     {
  30.         return [
  31.             CustomerEvents::CUSTOMER_WRITTEN_EVENT => 'onCustomerWritten',
  32.             CustomerEvents::CUSTOMER_DELETED_EVENT => 'onCustomerDeleted',
  33.         ];
  34.     }
  35.     public function onCustomerWritten(EntityWrittenEvent $event): void
  36.     {
  37.         foreach ($event->getWriteResults() as $writeResult) {
  38.             if ($writeResult->getOperation() !== EntityWriteResult::OPERATION_UPDATE) {
  39.                 continue;
  40.             }
  41.             $payload $writeResult->getPayload();
  42.             if (!$this->customerCredentialsChanged($payload)) {
  43.                 continue;
  44.             }
  45.             $customerId $payload['id'];
  46.             $newToken $this->invalidateUsingSession($customerId);
  47.             if ($newToken) {
  48.                 $this->contextPersister->revokeAllCustomerTokens($customerId$newToken);
  49.             } else {
  50.                 $this->contextPersister->revokeAllCustomerTokens($customerId);
  51.             }
  52.         }
  53.     }
  54.     public function onCustomerDeleted(EntityDeletedEvent $event): void
  55.     {
  56.         foreach ($event->getIds() as $customerId) {
  57.             $this->contextPersister->revokeAllCustomerTokens($customerId);
  58.         }
  59.     }
  60.     /**
  61.      * @param array<string, mixed> $payload
  62.      */
  63.     private function customerCredentialsChanged(array $payload): bool
  64.     {
  65.         return isset($payload['password']);
  66.     }
  67.     private function invalidateUsingSession(string $customerId): ?string
  68.     {
  69.         $master $this->requestStack->getMainRequest();
  70.         if (!$master) {
  71.             return null;
  72.         }
  73.         // Is not a storefront request
  74.         if (!$master->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) {
  75.             return null;
  76.         }
  77.         /** @var SalesChannelContext $context */
  78.         $context $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  79.         // Not loggedin skip
  80.         if ($context->getCustomer() === null) {
  81.             return null;
  82.         }
  83.         // The written customer is not the same as logged-in. We don't modify the user session
  84.         if ($context->getCustomer()->getId() !== $customerId) {
  85.             return null;
  86.         }
  87.         $token $context->getToken();
  88.         $newToken $this->contextPersister->replace($token$context);
  89.         $context->assign([
  90.             'token' => $newToken,
  91.         ]);
  92.         if (!$master->hasSession()) {
  93.             return null;
  94.         }
  95.         $session $master->getSession();
  96.         $session->migrate();
  97.         $session->set('sessionId'$session->getId());
  98.         $session->set(PlatformRequest::HEADER_CONTEXT_TOKEN$newToken);
  99.         $master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN$newToken);
  100.         return $newToken;
  101.     }
  102. }