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

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