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

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