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

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.         if ($token === false) {
  93.             throw new \LogicException('Unable to extract a JWT token from the request. Also, make sure to call `supports()` before `authenticate()` to get a proper client error.');
  94.         }
  95.         try {
  96.             if (!$payload $this->jwtManager->parse($token)) {
  97.                 throw new InvalidTokenException('Invalid JWT Token');
  98.             }
  99.         } catch (JWTDecodeFailureException $e) {
  100.             if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
  101.                 throw new ExpiredTokenException();
  102.             }
  103.             throw new InvalidTokenException('Invalid JWT Token'0$e);
  104.         }
  105.         $idClaim $this->jwtManager->getUserIdClaim();
  106.         if (!isset($payload[$idClaim])) {
  107.             throw new InvalidPayloadException($idClaim);
  108.         }
  109.         $passport = new SelfValidatingPassport(
  110.             new UserBadge(
  111.                 (string)$payload[$idClaim],
  112.                 function ($userIdentifier) use ($payload) {
  113.                     return $this->loadUser($payload$userIdentifier);
  114.                 }
  115.             )
  116.         );
  117.         $passport->setAttribute('payload'$payload);
  118.         $passport->setAttribute('token'$token);
  119.         return $passport;
  120.     }
  121.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?Response
  122.     {
  123.         return null;
  124.     }
  125.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception): ?Response
  126.     {
  127.         $errorMessage strtr($exception->getMessageKey(), $exception->getMessageData());
  128.         if (null !== $this->translator) {
  129.             $errorMessage $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security');
  130.         }
  131.         $response = new JWTAuthenticationFailureResponse($errorMessage);
  132.         if ($exception instanceof ExpiredTokenException) {
  133.             $event = new JWTExpiredEvent($exception$response$request);
  134.             $eventName Events::JWT_EXPIRED;
  135.         } else {
  136.             $event = new JWTInvalidEvent($exception$response$request);
  137.             $eventName Events::JWT_INVALID;
  138.         }
  139.         $this->eventDispatcher->dispatch($event$eventName);
  140.         return $event->getResponse();
  141.     }
  142.     /**
  143.      * Gets the token extractor to be used for retrieving a JWT token in the
  144.      * current request.
  145.      *
  146.      * Override this method for adding/removing extractors to the chain one or
  147.      * returning a different {@link TokenExtractorInterface} implementation.
  148.      */
  149.     protected function getTokenExtractor(): TokenExtractorInterface
  150.     {
  151.         return $this->tokenExtractor;
  152.     }
  153.     /**
  154.      * Gets the jwt manager.
  155.      */
  156.     protected function getJwtManager(): JWTTokenManagerInterface
  157.     {
  158.         return $this->jwtManager;
  159.     }
  160.     /**
  161.      * Gets the event dispatcher.
  162.      */
  163.     protected function getEventDispatcher(): EventDispatcherInterface
  164.     {
  165.         return $this->eventDispatcher;
  166.     }
  167.     /**
  168.      * Gets the user provider.
  169.      */
  170.     protected function getUserProvider(): UserProviderInterface
  171.     {
  172.         return $this->userProvider;
  173.     }
  174.     /**
  175.      * Loads the user to authenticate.
  176.      *
  177.      * @param array                 $payload      The token payload
  178.      * @param string                $identity     The key from which to retrieve the user "identifier"
  179.      */
  180.     protected function loadUser(array $payloadstring $identity): UserInterface
  181.     {
  182.         if ($this->userProvider instanceof PayloadAwareUserProviderInterface) {
  183.             if (method_exists($this->userProvider'loadUserByIdentifierAndPayload')) {
  184.                 return $this->userProvider->loadUserByIdentifierAndPayload($identity$payload);
  185.             } else {
  186.                 return $this->userProvider->loadUserByUsernameAndPayload($identity$payload);
  187.             }
  188.         }
  189.         if ($this->userProvider instanceof ChainUserProvider) {
  190.             foreach ($this->userProvider->getProviders() as $provider) {
  191.                 try {
  192.                     if ($provider instanceof PayloadAwareUserProviderInterface) {
  193.                         if (method_exists(PayloadAwareUserProviderInterface::class, 'loadUserByIdentifierAndPayload')) {
  194.                             return $provider->loadUserByIdentifierAndPayload($identity$payload);
  195.                         } else {
  196.                             return $provider->loadUserByUsernameAndPayload($identity$payload);
  197.                         }
  198.                     }
  199.                     return $provider->loadUserByIdentifier($identity);
  200.                     // More generic call to catch both UsernameNotFoundException for SF<5.3 and new UserNotFoundException
  201.                 } catch (AuthenticationException $e) {
  202.                     // try next one
  203.                 }
  204.             }
  205.             if (!class_exists(UserNotFoundException::class)) {
  206.                 $ex = new UsernameNotFoundException(sprintf('There is no user with username "%s".'$identity));
  207.                 $ex->setUsername($identity);
  208.             } else {
  209.                 $ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".'$identity));
  210.                 $ex->setUserIdentifier($identity);
  211.             }
  212.             throw $ex;
  213.         }
  214.         if (method_exists($this->userProvider'loadUserByIdentifier')) {
  215.             return $this->userProvider->loadUserByIdentifier($identity);
  216.         } else {
  217.             return $this->userProvider->loadUserByUsername($identity);
  218.         }
  219.     }
  220.     public function createAuthenticatedToken(PassportInterface $passportstring $firewallName): TokenInterface
  221.     {
  222.         if (!$passport instanceof Passport) {
  223.             throw new \LogicException(sprintf('Expected "%s" but got "%s".'Passport::class, get_debug_type($passport)));
  224.         }
  225.         $token = new JWTPostAuthenticationToken($passport->getUser(), $firewallName$passport->getUser()->getRoles(), $passport->getAttribute('token'));
  226.         $this->eventDispatcher->dispatch(new JWTAuthenticatedEvent($passport->getAttribute('payload'), $token), Events::JWT_AUTHENTICATED);
  227.         return $token;
  228.     }
  229.     public function createToken(Passport $passportstring $firewallName): TokenInterface
  230.     {
  231.         $token = new JWTPostAuthenticationToken($passport->getUser(), $firewallName$passport->getUser()->getRoles(), $passport->getAttribute('token'));
  232.         $this->eventDispatcher->dispatch(new JWTAuthenticatedEvent($passport->getAttribute('payload'), $token), Events::JWT_AUTHENTICATED);
  233.         return $token;
  234.     }
  235. }