src/Domain/User/Symfony/EventSubscriber/Security/AuthenticationSubscriber.php line 95

Open in your IDE?
  1. <?php
  2. namespace App\Domain\User\Symfony\EventSubscriber\Security;
  3. use App\Domain\User\Exception\ExceededLoginAttemptsException;
  4. use App\Domain\User\Model\LoginAttempt;
  5. use App\Domain\User\Repository\LoginAttempt as LoginAttemptRepository;
  6. use App\Domain\User\Repository\User as UserRepository;
  7. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  8. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  9. use Symfony\Component\HttpFoundation\RequestStack;
  10. use Symfony\Component\Security\Core\AuthenticationEvents;
  11. use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
  12. use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
  13. class AuthenticationSubscriber implements EventSubscriberInterface
  14. {
  15.     protected RequestStack $requestStack;
  16.     protected LoginAttemptRepository $loginAttemptRepository;
  17.     protected UserRepository $userRepository;
  18.     protected int $maxAttempts;
  19.     protected EventDispatcherInterface $dispatcher;
  20.     public function __construct(
  21.         RequestStack $requestStack,
  22.         LoginAttemptRepository $loginAttemptRepository,
  23.         UserRepository $userRepository,
  24.         int $maxAttempts,
  25.         EventDispatcherInterface $dispatcher
  26.     ) {
  27.         $this->requestStack           $requestStack;
  28.         $this->loginAttemptRepository $loginAttemptRepository;
  29.         $this->userRepository         $userRepository;
  30.         $this->maxAttempts            $maxAttempts;
  31.         $this->dispatcher             $dispatcher;
  32.     }
  33.     public static function getSubscribedEvents(): array
  34.     {
  35.         return [
  36.             AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthFailure',
  37.             AuthenticationEvents::AUTHENTICATION_SUCCESS => 'onAuthSuccess',
  38.         ];
  39.     }
  40.     public function onAuthFailure(AuthenticationFailureEvent $event)
  41.     {
  42.         $ip      $this->requestStack->getCurrentRequest()->getClientIp();
  43.         $email   $event->getAuthenticationToken()->getUsername();
  44.         $attempt $this->loginAttemptRepository->findOneOrNullByIpAndEmail($ip$email);
  45.         // If this is the first login attempt, create new entity
  46.         if (null === $attempt) {
  47.             $attempt = new LoginAttempt();
  48.             $attempt->setAttempts(0);
  49.             $attempt->setIp($ip);
  50.             $attempt->setEmail($email);
  51.             $this->loginAttemptRepository->insert($attempt);
  52.         }
  53.         $n $attempt->getAttempts();
  54.         $attempt->setAttempts($attempt->getAttempts() + 1);
  55.         if ($attempt->getAttempts() > $this->maxAttempts) {
  56.             $user $this->userRepository->findOneOrNullByEmail($email);
  57.             // Disable this user if it exists and the maximum number of login attempts was exceeded
  58.             if ($user) {
  59.                 $user->setEnabled(false);
  60.                 $attempt->setAttempts(0);
  61.                 $this->loginAttemptRepository->update($attempt);
  62.                 $this->userRepository->update($user);
  63.                 throw new ExceededLoginAttemptsException();
  64.             }
  65.         }
  66.         $this->loginAttemptRepository->update($attempt);
  67.         // Exponential wait time for wrong passwords
  68.         sleep($n);
  69.     }
  70.     public function onAuthSuccess(AuthenticationSuccessEvent $event)
  71.     {
  72.         if (!$event->getAuthenticationToken()) {
  73.             return;
  74.         }
  75.         $user $event->getAuthenticationToken()->getUser();
  76.         if (is_string($user)) {
  77.             return;
  78.         }
  79.         $ip      $this->requestStack->getCurrentRequest()->getClientIp();
  80.         $email   $event->getAuthenticationToken()->getUsername();
  81.         $attempt $this->loginAttemptRepository->findOneOrNullByIpAndEmail($ip$email);
  82.         // If the attempt exists, we reset the number of attempts to zero on successful login.
  83.         if ($attempt) {
  84.             $attempt->setAttempts(0);
  85.             $this->loginAttemptRepository->update($attempt);
  86.         }
  87.     }
  88. }