vendor/lexik/jwt-authentication-bundle/Security/Authenticator/JWTAuthenticator.php line 128

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator;
  4. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
  5. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTExpiredEvent;
  6. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
  7. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
  8. use Lexik\Bundle\JWTAuthenticationBundle\Events;
  9. use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
  10. use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidPayloadException;
  11. use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
  12. use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
  13. use Lexik\Bundle\JWTAuthenticationBundle\Exception\MissingTokenException;
  14. use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
  15. use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\Token\JWTPostAuthenticationToken;
  16. use Lexik\Bundle\JWTAuthenticationBundle\Security\User\PayloadAwareUserProviderInterface;
  17. use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
  18. use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
  19. use Symfony\Component\HttpFoundation\Request;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  22. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  23. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  24. use Symfony\Component\Security\Core\Exception\UserNotFoundException;
  25. use Symfony\Component\Security\Core\User\ChainUserProvider;
  26. use Symfony\Component\Security\Core\User\UserInterface;
  27. use Symfony\Component\Security\Core\User\UserProviderInterface;
  28. use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
  29. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  30. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  31. use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
  32. use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
  33. use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
  34. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  35. use Symfony\Contracts\Translation\TranslatorInterface;
  36. class JWTAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface
  37. {
  38.     use ForwardCompatAuthenticatorTrait;
  39.     /**
  40.      * @var TokenExtractorInterface
  41.      */
  42.     private  $tokenExtractor;
  43.     /**
  44.      * @var JWTTokenManagerInterface
  45.      */
  46.     private $jwtManager;
  47.     /**
  48.      * @var EventDispatcherInterface
  49.      */
  50.     private $eventDispatcher;
  51.     /**
  52.      * @var UserProviderInterface
  53.      */
  54.     private $userProvider;
  55.     /**
  56.      * @var TranslatorInterface|null
  57.      */
  58.     private $translator;
  59.     public function __construct(
  60.         JWTTokenManagerInterface $jwtManager,
  61.         EventDispatcherInterface $eventDispatcher,
  62.         TokenExtractorInterface $tokenExtractor,
  63.         UserProviderInterface $userProvider,
  64.         TranslatorInterface $translator null
  65.     ) {
  66.         $this->tokenExtractor $tokenExtractor;
  67.         $this->jwtManager $jwtManager;
  68.         $this->eventDispatcher $eventDispatcher;
  69.         $this->userProvider $userProvider;
  70.         $this->translator $translator;
  71.     }
  72.     /**
  73.      * {@inheritdoc}
  74.      */
  75.     public function start(Request $requestAuthenticationException $authException null): Response
  76.     {
  77.         $exception = new MissingTokenException('JWT Token not found'0$authException);
  78.         $event = new JWTNotFoundEvent($exception, new JWTAuthenticationFailureResponse($exception->getMessageKey()), $request);
  79.         $this->eventDispatcher->dispatch($eventEvents::JWT_NOT_FOUND);
  80.         return $event->getResponse();
  81.     }
  82.     public function supports(Request $request): ?bool
  83.     {
  84.         return false !== $this->getTokenExtractor()->extract($request);
  85.     }
  86.     /**
  87.      * @return Passport
  88.      */
  89.     public function doAuthenticate(Request $request/*: Passport */
  90.     {
  91.         $token $this->getTokenExtractor()->extract($request);
  92.         try {
  93.             if (!$payload $this->jwtManager->parse($token)) {
  94.                 throw new InvalidTokenException('Invalid JWT Token');
  95.             }
  96.         } catch (JWTDecodeFailureException $e) {
  97.             if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
  98.                 throw new ExpiredTokenException();
  99.             }
  100.             throw new InvalidTokenException('Invalid JWT Token'0$e);
  101.         }
  102.         $idClaim $this->jwtManager->getUserIdClaim();
  103.         if (!isset($payload[$idClaim])) {
  104.             throw new InvalidPayloadException($idClaim);
  105.         }
  106.         $passport = new SelfValidatingPassport(
  107.             new UserBadge((string)$payload[$idClaim],
  108.             function ($userIdentifier) use($payload) {
  109.                 return $this->loadUser($payload$userIdentifier);
  110.             })
  111.         );
  112.         $passport->setAttribute('payload'$payload);
  113.         $passport->setAttribute('token'$token);
  114.         return $passport;
  115.     }
  116.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?Response
  117.     {
  118.         return null;
  119.     }
  120.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception): ?Response
  121.     {
  122.         $errorMessage strtr($exception->getMessageKey(), $exception->getMessageData());
  123.         if (null !== $this->translator) {
  124.             $errorMessage $this->translator->trans($exception->getMessageKey(), $exception->getMessageData());
  125.         }
  126.         $response = new JWTAuthenticationFailureResponse($errorMessage);
  127.         if ($exception instanceof ExpiredTokenException) {
  128.             $event = new JWTExpiredEvent($exception$response$request);
  129.             $eventName Events::JWT_EXPIRED;
  130.         } else {
  131.             $event = new JWTInvalidEvent($exception$response$request);
  132.             $eventName Events::JWT_INVALID;
  133.         }
  134.         $this->eventDispatcher->dispatch($event$eventName);
  135.         return $event->getResponse();
  136.     }
  137.     /**
  138.      * Gets the token extractor to be used for retrieving a JWT token in the
  139.      * current request.
  140.      *
  141.      * Override this method for adding/removing extractors to the chain one or
  142.      * returning a different {@link TokenExtractorInterface} implementation.
  143.      */
  144.     protected function getTokenExtractor(): TokenExtractorInterface
  145.     {
  146.         return $this->tokenExtractor;
  147.     }
  148.     /**
  149.      * Gets the jwt manager.
  150.      */
  151.     protected function getJwtManager(): JWTTokenManagerInterface
  152.     {
  153.         return $this->jwtManager;
  154.     }
  155.     /**
  156.      * Gets the event dispatcher.
  157.      */
  158.     protected function getEventDispatcher(): EventDispatcherInterface
  159.     {
  160.         return $this->eventDispatcher;
  161.     }
  162.     /**
  163.      * Gets the user provider.
  164.      */
  165.     protected function getUserProvider(): UserProviderInterface
  166.     {
  167.         return $this->userProvider;
  168.     }
  169.     /**
  170.      * Loads the user to authenticate.
  171.      *
  172.      * @param array                 $payload      The token payload
  173.      * @param string                $identity     The key from which to retrieve the user "identifier"
  174.      */
  175.     protected function loadUser(array $payloadstring $identity): UserInterface
  176.     {
  177.         if ($this->userProvider instanceof PayloadAwareUserProviderInterface) {
  178.             if (method_exists($this->userProvider'loadUserByIdentifierAndPayload')) {
  179.                 return $this->userProvider->loadUserByIdentifierAndPayload($identity$payload);
  180.             } else {
  181.                 return $this->userProvider->loadUserByUsernameAndPayload($identity$payload);
  182.             }
  183.         }
  184.         if ($this->userProvider instanceof ChainUserProvider) {
  185.             foreach ($this->userProvider->getProviders() as $provider) {
  186.                 try {
  187.                     if ($provider instanceof PayloadAwareUserProviderInterface) {
  188.                         if (method_exists(PayloadAwareUserProviderInterface::class, 'loadUserByIdentifierAndPayload')) {
  189.                             return $provider->loadUserByIdentifierAndPayload($identity$payload);
  190.                         } else {
  191.                             return $provider->loadUserByUsernameAndPayload($identity$payload);
  192.                         }
  193.                     }
  194.                     return $provider->loadUserByIdentifier($identity);
  195.                 // More generic call to catch both UsernameNotFoundException for SF<5.3 and new UserNotFoundException
  196.                 } catch (AuthenticationException $e) {
  197.                     // try next one
  198.                 }
  199.             }
  200.             if(!class_exists(UserNotFoundException::class)) {
  201.                 $ex = new UsernameNotFoundException(sprintf('There is no user with username "%s".'$identity));
  202.                 $ex->setUsername($identity);
  203.             } else {
  204.                 $ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".'$identity));
  205.                 $ex->setUserIdentifier($identity);
  206.             }
  207.             throw $ex;
  208.         }
  209.         if (method_exists($this->userProvider'loadUserByIdentifier')) {
  210.             return $this->userProvider->loadUserByIdentifier($identity);
  211.         } else {
  212.             return $this->userProvider->loadUserByUsername($identity);
  213.         }
  214.     }
  215.     public function createAuthenticatedToken(PassportInterface $passportstring $firewallName): TokenInterface
  216.     {
  217.         if (!$passport instanceof Passport) {
  218.             throw new \LogicException(sprintf('Expected "%s" but got "%s".'Passport::class, get_debug_type($passport)));
  219.         }
  220.         $token = new JWTPostAuthenticationToken($passport->getUser(), $firewallName$passport->getUser()->getRoles(), $passport->getAttribute('token'));
  221.         $this->eventDispatcher->dispatch(new JWTAuthenticatedEvent($passport->getAttribute('payload'), $token), Events::JWT_AUTHENTICATED);
  222.         return $token;
  223.     }
  224.     public function createToken(Passport $passportstring $firewallName): TokenInterface
  225.     {
  226.         $token = new JWTPostAuthenticationToken($passport->getUser(), $firewallName$passport->getUser()->getRoles(), $passport->getAttribute('token'));
  227.         $this->eventDispatcher->dispatch(new JWTAuthenticatedEvent($passport->getAttribute('payload'), $token), Events::JWT_AUTHENTICATED);
  228.         return $token;
  229.     }
  230. }