vendor/lexik/jwt-authentication-bundle/Security/Guard/JWTTokenAuthenticator.php line 224

Open in your IDE?
  1. <?php
  2. namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Guard;
  3. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
  4. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTExpiredEvent;
  5. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
  6. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
  7. use Lexik\Bundle\JWTAuthenticationBundle\Events;
  8. use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
  9. use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidPayloadException;
  10. use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
  11. use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
  12. use Lexik\Bundle\JWTAuthenticationBundle\Exception\MissingTokenException;
  13. use Lexik\Bundle\JWTAuthenticationBundle\Exception\UserNotFoundException;
  14. use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
  15. use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
  16. use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken;
  17. use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserTokenInterface;
  18. use Lexik\Bundle\JWTAuthenticationBundle\Security\User\PayloadAwareUserProviderInterface;
  19. use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
  20. use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  23. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  24. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  25. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  26. use Symfony\Component\Security\Core\Exception\UserNotFoundException as SecurityUserNotFoundException;
  27. use Symfony\Component\Security\Core\User\ChainUserProvider;
  28. use Symfony\Component\Security\Core\User\UserInterface;
  29. use Symfony\Component\Security\Core\User\UserProviderInterface;
  30. use Symfony\Component\Security\Guard\AuthenticatorInterface;
  31. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  32. use Symfony\Contracts\Translation\TranslatorInterface;
  33. /**
  34.  * JWTTokenAuthenticator (Guard implementation).
  35.  *
  36.  * @see http://knpuniversity.com/screencast/symfony-rest4/jwt-guard-authenticator
  37.  *
  38.  * @author Nicolas Cabot <n.cabot@lexik.fr>
  39.  * @author Robin Chalas <robin.chalas@gmail.com>
  40.  */
  41. class JWTTokenAuthenticator implements AuthenticatorInterface
  42. {
  43.     /**
  44.      * @var JWTTokenManagerInterface
  45.      */
  46.     private $jwtManager;
  47.     /**
  48.      * @var EventDispatcherInterface
  49.      */
  50.     private $dispatcher;
  51.     /**
  52.      * @var TokenExtractorInterface
  53.      */
  54.     private $tokenExtractor;
  55.     /**
  56.      * @var TokenStorageInterface
  57.      */
  58.     private $preAuthenticationTokenStorage;
  59.     /**
  60.      * @var TranslatorInterface
  61.      */
  62.     private $translator;
  63.     public function __construct(
  64.         JWTTokenManagerInterface $jwtManager,
  65.         EventDispatcherInterface $dispatcher,
  66.         TokenExtractorInterface $tokenExtractor,
  67.         TokenStorageInterface $preAuthenticationTokenStorage,
  68.         TranslatorInterface $translator null
  69.     ) {
  70.         $this->jwtManager $jwtManager;
  71.         $this->dispatcher $dispatcher;
  72.         $this->tokenExtractor $tokenExtractor;
  73.         $this->preAuthenticationTokenStorage $preAuthenticationTokenStorage;
  74.         $this->translator $translator;
  75.     }
  76.     public function supports(Request $request)
  77.     {
  78.         return false !== $this->getTokenExtractor()->extract($request);
  79.     }
  80.     /**
  81.      * Returns a decoded JWT token extracted from a request.
  82.      *
  83.      * {@inheritdoc}
  84.      *
  85.      * @return PreAuthenticationJWTUserTokenInterface
  86.      *
  87.      * @throws InvalidTokenException If an error occur while decoding the token
  88.      * @throws ExpiredTokenException If the request token is expired
  89.      */
  90.     public function getCredentials(Request $request)
  91.     {
  92.         $tokenExtractor $this->getTokenExtractor();
  93.         if (!$tokenExtractor instanceof TokenExtractorInterface) {
  94.             throw new \RuntimeException(sprintf('Method "%s::getTokenExtractor()" must return an instance of "%s".'self::class, TokenExtractorInterface::class));
  95.         }
  96.         if (false === ($jsonWebToken $tokenExtractor->extract($request))) {
  97.             return;
  98.         }
  99.         $preAuthToken = new PreAuthenticationJWTUserToken($jsonWebToken);
  100.         try {
  101.             if (!$payload $this->jwtManager->decode($preAuthToken)) {
  102.                 throw new InvalidTokenException('Invalid JWT Token');
  103.             }
  104.             $preAuthToken->setPayload($payload);
  105.         } catch (JWTDecodeFailureException $e) {
  106.             if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
  107.                 $expiredTokenException = new ExpiredTokenException();
  108.                 $expiredTokenException->setToken($preAuthToken);
  109.                 throw $expiredTokenException;
  110.             }
  111.             throw new InvalidTokenException('Invalid JWT Token'0$e);
  112.         }
  113.         return $preAuthToken;
  114.     }
  115.     /**
  116.      * Returns an user object loaded from a JWT token.
  117.      *
  118.      * {@inheritdoc}
  119.      *
  120.      * @param PreAuthenticationJWTUserTokenInterface $preAuthToken Implementation of the (Security) TokenInterface
  121.      *
  122.      * @throws \InvalidArgumentException If preAuthToken is not of the good type
  123.      * @throws InvalidPayloadException   If the user identity field is not a key of the payload
  124.      * @throws UserNotFoundException     If no user can be loaded from the given token
  125.      */
  126.     public function getUser($preAuthTokenUserProviderInterface $userProvider)
  127.     {
  128.         if (!$preAuthToken instanceof PreAuthenticationJWTUserTokenInterface) {
  129.             throw new \InvalidArgumentException(sprintf('The first argument of the "%s()" method must be an instance of "%s".'__METHOD__PreAuthenticationJWTUserTokenInterface::class));
  130.         }
  131.         $payload $preAuthToken->getPayload();
  132.         $idClaim $this->jwtManager->getUserIdClaim();
  133.         if (!isset($payload[$idClaim])) {
  134.             throw new InvalidPayloadException($idClaim);
  135.         }
  136.         $user $this->loadUser($userProvider$payload$payload[$idClaim]);
  137.         $this->preAuthenticationTokenStorage->setToken($preAuthToken);
  138.         return $user;
  139.     }
  140.     /**
  141.      * {@inheritdoc}
  142.      */
  143.     public function onAuthenticationFailure(Request $requestAuthenticationException $authException)
  144.     {
  145.         $errorMessage strtr($authException->getMessageKey(), $authException->getMessageData());
  146.         if (null !== $this->translator) {
  147.             $errorMessage $this->translator->trans($authException->getMessageKey(), $authException->getMessageData(), 'security');
  148.         }
  149.         $response = new JWTAuthenticationFailureResponse($errorMessage);
  150.         if ($authException instanceof ExpiredTokenException) {
  151.             $event = new JWTExpiredEvent($authException$response$request);
  152.             $eventName Events::JWT_EXPIRED;
  153.         } else {
  154.             $event = new JWTInvalidEvent($authException$response$request);
  155.             $eventName Events::JWT_INVALID;
  156.         }
  157.         $this->dispatcher->dispatch($event$eventName);
  158.         return $event->getResponse();
  159.     }
  160.     /**
  161.      * {@inheritdoc}
  162.      */
  163.     public function onAuthenticationSuccess(Request $requestTokenInterface $token$providerKey)
  164.     {
  165.         return;
  166.     }
  167.     /**
  168.      * {@inheritdoc}
  169.      *
  170.      * @return JWTAuthenticationFailureResponse
  171.      */
  172.     public function start(Request $requestAuthenticationException $authException null)
  173.     {
  174.         $exception = new MissingTokenException('JWT Token not found'0$authException);
  175.         $event = new JWTNotFoundEvent($exception, new JWTAuthenticationFailureResponse($exception->getMessageKey()), $request);
  176.         $this->dispatcher->dispatch($eventEvents::JWT_NOT_FOUND);
  177.         return $event->getResponse();
  178.     }
  179.     /**
  180.      * {@inheritdoc}
  181.      */
  182.     public function checkCredentials($credentialsUserInterface $user)
  183.     {
  184.         return true;
  185.     }
  186.     /**
  187.      * {@inheritdoc}
  188.      *
  189.      * @throws \RuntimeException If there is no pre-authenticated token previously stored
  190.      */
  191.     public function createAuthenticatedToken(UserInterface $user$providerKey)
  192.     {
  193.         $preAuthToken $this->preAuthenticationTokenStorage->getToken();
  194.         if (null === $preAuthToken) {
  195.             throw new \RuntimeException('Unable to return an authenticated token since there is no pre authentication token.');
  196.         }
  197.         $authToken = new JWTUserToken($user->getRoles(), $user$preAuthToken->getCredentials(), $providerKey);
  198.         $this->dispatcher->dispatch(new JWTAuthenticatedEvent($preAuthToken->getPayload(), $authToken), Events::JWT_AUTHENTICATED);
  199.         $this->preAuthenticationTokenStorage->setToken(null);
  200.         return $authToken;
  201.     }
  202.     /**
  203.      * {@inheritdoc}
  204.      */
  205.     public function supportsRememberMe()
  206.     {
  207.         return false;
  208.     }
  209.     /**
  210.      * Gets the token extractor to be used for retrieving a JWT token in the
  211.      * current request.
  212.      *
  213.      * Override this method for adding/removing extractors to the chain one or
  214.      * returning a different {@link TokenExtractorInterface} implementation.
  215.      *
  216.      * @return TokenExtractorInterface
  217.      */
  218.     protected function getTokenExtractor()
  219.     {
  220.         return $this->tokenExtractor;
  221.     }
  222.     /**
  223.      * @return JWTTokenManagerInterface
  224.      */
  225.     protected function getJwtManager()
  226.     {
  227.         return $this->jwtManager;
  228.     }
  229.     /**
  230.      * @return EventDispatcherInterface
  231.      */
  232.     protected function getDispatcher()
  233.     {
  234.         return $this->dispatcher;
  235.     }
  236.     /**
  237.      * @return TokenStorageInterface
  238.      */
  239.     protected function getPreAuthenticationTokenStorage()
  240.     {
  241.         return $this->preAuthenticationTokenStorage;
  242.     }
  243.     /**
  244.      * Loads the user to authenticate.
  245.      *
  246.      * @param UserProviderInterface $userProvider An user provider
  247.      * @param array                 $payload      The token payload
  248.      * @param string                $identity     The key from which to retrieve the user "username"
  249.      *
  250.      * @return UserInterface
  251.      */
  252.     protected function loadUser(UserProviderInterface $userProvider, array $payload$identity)
  253.     {
  254.         $providers $userProvider instanceof ChainUserProvider $userProvider->getProviders() : [$userProvider];
  255.         foreach ($providers as $provider) {
  256.             try {
  257.                 if ($provider instanceof PayloadAwareUserProviderInterface) {
  258.                     return $provider->loadUserByUsernameAndPayload($identity$payload);
  259.                 }
  260.                 if (method_exists($provider'loadUserByIdentifier')) {
  261.                     return $provider->loadUserByIdentifier($identity);
  262.                 }
  263.                 return $provider->loadUserByUsername($identity);
  264.             } catch (SecurityUserNotFoundException UsernameNotFoundException $e) {
  265.                 // try next one
  266.             }
  267.         }
  268.         if (class_exists(SecurityUserNotFoundException::class)) {
  269.             $ex = new SecurityUserNotFoundException(sprintf('There is no user with name "%s".'$identity));
  270.             $ex->setUserIdentifier($identity);
  271.         } else {
  272.             $ex = new UsernameNotFoundException(sprintf('There is no user with name "%s".'$identity));
  273.             $ex->setUsername($identity);
  274.         }
  275.         throw $ex;
  276.     }
  277. }