src/App/Security/TwoFactor/TwoFactorAuthSubscriber.php line 50

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\Security\TwoFactor;
  3. use App\Security\TwoFactor\AllowedDevice\AllowedDeviceAuthenticator;
  4. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  5. use Symfony\Component\HttpFoundation\RedirectResponse;
  6. use Symfony\Component\HttpKernel\Event\RequestEvent;
  7. use Symfony\Component\HttpKernel\HttpKernelInterface;
  8. use Symfony\Component\Routing\RouterInterface;
  9. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  10. class TwoFactorAuthSubscriber implements EventSubscriberInterface
  11. {
  12.     private bool $twoFactorEnabled;
  13.     private TokenStorageInterface $tokenStorage;
  14.     private TwoFactorManager $twoFactorManager;
  15.     private RouterInterface $router;
  16.     private AllowedDeviceAuthenticator $rememberedDeviceAuthenticator;
  17.     private const ROUTES = [
  18.         'two_factor_setup',
  19.         'two_factor_verify',
  20.         'two_factor_notice',
  21.         'backend_two_factor_setup',
  22.         'backend_two_factor_verify',
  23.         'backend_two_factor_notice',
  24.     ];
  25.     public function __construct(
  26.         TokenStorageInterface $tokenStorage,
  27.         TwoFactorManager $twoFactorManager,
  28.         RouterInterface $router,
  29.         AllowedDeviceAuthenticator $rememberedDeviceAuthenticator,
  30.         bool $twoFactorEnabled
  31.     )
  32.     {
  33.         $this->tokenStorage $tokenStorage;
  34.         $this->twoFactorManager $twoFactorManager;
  35.         $this->router $router;
  36.         $this->rememberedDeviceAuthenticator $rememberedDeviceAuthenticator;
  37.         $this->twoFactorEnabled $twoFactorEnabled;
  38.     }
  39.     public function onKernelRequest(RequestEvent $event): void
  40.     {
  41.         if ($event->getRequestType() !== HttpKernelInterface::MAIN_REQUEST) {
  42.             return;
  43.         }
  44.         /*
  45.          * Bypass listener when:
  46.          * - user is not logged in or capable of two factor auth.
  47.          * - requesting any of the two factor routes.
  48.          * - satellite has disabled two factor auth.
  49.          * - user is using remembered device
  50.          */
  51.         if (!$this->twoFactorEnabled) {
  52.             return;
  53.         }
  54.         $token $this->tokenStorage->getToken();
  55.         /** @var TwoFactorUserInterface|null $user */
  56.         $user = (null !== $token && $token->getUser() instanceof TwoFactorUserInterface) ? $token->getUser() : null;
  57.         if (null === $user) {
  58.             return;
  59.         }
  60.         $route $event->getRequest()->attributes->get('_route');
  61.         if (in_array($routeself::ROUTEStrue)) {
  62.             return;
  63.         }
  64.         if ($this->rememberedDeviceAuthenticator->authenticate($event->getRequest())) {
  65.             return;
  66.         }
  67.         /*
  68.          * If user is not authenticated, redirect him based on configuration...
  69.          */
  70.         if ($this->twoFactorManager->isAuthenticated($user$event->getRequest())) {
  71.             return;
  72.         }
  73.         if ($user->isTwoFactorVerified()) {
  74.             /*
  75.              * This means user is fully capable of authenticating via two factor auth...
  76.              */
  77.             $event->setResponse(new RedirectResponse(
  78.                 $this->router->generate('two_factor_verify', ['continue' => $event->getRequest()->getRequestUri()])
  79.             ));
  80.         } else {
  81.             /*
  82.              * This means satellite requires users to set up 2fa...
  83.              * Redirect to notice page which prompts user to set up 2fa...
  84.              */
  85.             $event->setResponse(new RedirectResponse(
  86.                 $this->router->generate('two_factor_notice', ['continue' => $event->getRequest()->getRequestUri()])
  87.             ));
  88.         }
  89.         /*
  90.          * Do nothing and allow user trough if satellite does not require 2fa.
  91.          */
  92.     }
  93.     /**
  94.      * @return array<string, array<int|string, array<int|string, int|string>|int|string>|string>
  95.      */
  96.     public static function getSubscribedEvents(): array
  97.     {
  98.         return [
  99.             RequestEvent::class => 'onKernelRequest',
  100.         ];
  101.     }
  102. }