src/EventSubscriber/LogoutSubscriber.php line 50

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the nellapp-core package.
  4. *
  5. * (c) Benjamin Georgeault
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace App\EventSubscriber;
  11. use App\Entity\Account\SsoAppSession;
  12. use App\Entity\Account\User;
  13. use App\Repository\Account\SsoAppSessionRepository;
  14. use Nellapp\Bundle\SDKBundle\Routing\UrlGeneratorInterface;
  15. use Psr\Log\LoggerInterface;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\HttpFoundation\RedirectResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  20. use Symfony\Component\Security\Http\Event\LogoutEvent;
  21. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  22. use Symfony\Contracts\HttpClient\HttpClientInterface;
  23. /**
  24. * Class LogoutSubscriber
  25. *
  26. * @author Benjamin Georgeault
  27. */
  28. class LogoutSubscriber implements EventSubscriberInterface
  29. {
  30. public function __construct(
  31. private SsoAppSessionRepository $ssoAppSessionRepository,
  32. private HttpClientInterface $httpClient,
  33. private UrlGeneratorInterface $internalUrlGenerator,
  34. private LoggerInterface $logger,
  35. )
  36. {
  37. }
  38. public static function getSubscribedEvents(): array
  39. {
  40. return [
  41. LogoutEvent::class => 'logout',
  42. ];
  43. }
  44. public function logout(LogoutEvent $event): void
  45. {
  46. if (null === $user = $this->getUser($event->getToken())) {
  47. return;
  48. }
  49. $request = $event->getRequest();
  50. $this->triggerGlobalLogout($user, $request);
  51. if (!$request->query->has('target')) {
  52. return;
  53. }
  54. if (!filter_var($target = $request->query->get('target'), FILTER_VALIDATE_URL)) {
  55. return;
  56. }
  57. $event->setResponse(new RedirectResponse($target));
  58. }
  59. private function getUser(TokenInterface $token): ?User
  60. {
  61. if (($user = $token->getUser()) instanceof User) {
  62. return $user;
  63. }
  64. return null;
  65. }
  66. private function triggerGlobalLogout(User $user, Request $request): void
  67. {
  68. $deviceId = $request->cookies->get('DEVICE_ID');
  69. if (!$deviceId) {
  70. return;
  71. }
  72. $sessions = $this->ssoAppSessionRepository->findActiveByUserAndDevice($user, $deviceId);
  73. /** Should only have one active session by app and device */
  74. /** @var SsoAppSession $session */
  75. foreach ($sessions as $session) {
  76. $app = $session->getApp();
  77. $payload = [
  78. 'sessionIds' => [$session->getSessionId()],
  79. ];
  80. try {
  81. if ($this->internalUrlGenerator->exist($app->getQueueName(), 'api_account_global_logout')) {
  82. $this->httpClient->request(
  83. 'POST',
  84. $this->internalUrlGenerator->generate($app->getQueueName(), 'api_account_global_logout'),
  85. [
  86. 'json' => $payload,
  87. 'headers' => [
  88. 'Authorization' => 'Core ' . $app->getSecret(),
  89. ]
  90. ]
  91. );
  92. }
  93. } catch (TransportExceptionInterface $e) {
  94. $this->logger->error($e);
  95. }
  96. $this->ssoAppSessionRepository->revokeSession($session);
  97. }
  98. }
  99. }