src/Security/Authentication/JWTSubscriber.php line 44

  1. <?php
  2. namespace App\Security\Authentication;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface,
  4.     Symfony\Component\HttpFoundation\RequestStack,
  5.     Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  6. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent,
  7.     Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent,
  8.     Lexik\Bundle\JWTAuthenticationBundle\Events as LexikEvents,
  9.     Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface,
  10.     Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\Token\JWTPostAuthenticationToken;
  11. final class JWTSubscriber implements EventSubscriberInterface
  12. {
  13.     const REMEMBER_ME_TTL '30 days';
  14.     private TokenStorageInterface $tokenStorage;
  15.     private JWTTokenManagerInterface $jwtManager;
  16.     private RequestStack $requestStack;
  17.     public function __construct(
  18.         TokenStorageInterface $tokenStorage,
  19.         JWTTokenManagerInterface $jwtManager,
  20.         RequestStack $requestStack
  21.     ) {
  22.         $this->tokenStorage $tokenStorage;
  23.         $this->jwtManager $jwtManager;
  24.         $this->requestStack $requestStack;
  25.     }
  26.     public static function getSubscribedEvents() : array
  27.     {
  28.         return [
  29.             LexikEvents::JWT_CREATED => [
  30.                 ['onJWTCreated'10],
  31.             ],
  32.             // LexikEvents::JWT_DECODED => [
  33.             //     ['onJWTDecoded', 10]
  34.             // ]
  35.         ];
  36.     }
  37.     public function onJWTCreated(JWTCreatedEvent $event): void
  38.     {
  39.         $payload $event->getData();
  40.         $payload['ip'] = $this->getClientIp();
  41.         $event->setData($payload);
  42.         $header $event->getHeader();
  43.         $header['cty'] = 'JWT';
  44.         $event->setHeader($header);
  45.         $exp $this->getExpire();
  46.         if ($exp) {
  47.             $payload['exp'] = $exp;
  48.         }
  49.         $event->setData($payload);
  50.         return;
  51.     }
  52.     public function onJWTDecoded(JWTDecodedEvent $event): void
  53.     {
  54.         $payload $event->getPayload();
  55.         if (! isset($payload['ip']) || $payload['ip'] !== $this->getClientIp()) {
  56.             $event->markAsInvalid();
  57.         }
  58.         return;
  59.     }
  60.     private function getExpire(): ?string
  61.     {
  62.         $currentToken $this->tokenStorage->getToken();
  63.         if ($currentToken instanceof JWTPostAuthenticationToken) {
  64.             $decodedToken $this->jwtManager->decode($currentToken);
  65.             $expire time() + $decodedToken['exp'] - $decodedToken['iat'];
  66.             return $expire;
  67.         }
  68.         $data json_decode($this->getRequestContent()) ?? new \stdClass();
  69.         if (! property_exists($data'_remember_me') || ! $data->_remember_me) {
  70.             return null;
  71.         }
  72.         return (new \DateTimeImmutable('+ ' self::REMEMBER_ME_TTL))
  73.             ->getTimestamp();
  74.     }
  75.     private function getClientIp(): string
  76.     {
  77.         return $this->requestStack->getCurrentRequest()?->getClientIp() ?? '';
  78.     }
  79.     private function getRequestContent(): string
  80.     {
  81.         return $this->requestStack->getCurrentRequest()?->getContent() ?? '';
  82.     }
  83. }