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

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\EventDispatcher\EventDispatcherInterface;
  22. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
  23. use Symfony\Component\HttpFoundation\Request;
  24. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
  25. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  26. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  27. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  28. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  29. use Symfony\Component\Security\Core\User\ChainUserProvider;
  30. use Symfony\Component\Security\Core\User\UserInterface;
  31. use Symfony\Component\Security\Core\User\UserProviderInterface;
  32. use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
  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 extends AbstractGuardAuthenticator
  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.      * @param JWTTokenManagerInterface $jwtManager
  61.      * @param EventDispatcherInterface $dispatcher
  62.      * @param TokenExtractorInterface  $tokenExtractor
  63.      * @param TokenStorageInterface    $preAuthenticationTokenStorage
  64.      */
  65.     public function __construct(
  66.         JWTTokenManagerInterface $jwtManager,
  67.         EventDispatcherInterface $dispatcher,
  68.         TokenExtractorInterface $tokenExtractor,
  69.         TokenStorageInterface $preAuthenticationTokenStorage
  70.     ) {
  71.         $this->jwtManager                    $jwtManager;
  72.         $this->dispatcher                    $dispatcher;
  73.         $this->tokenExtractor                $tokenExtractor;
  74.         $this->preAuthenticationTokenStorage $preAuthenticationTokenStorage;
  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".'__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 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(
  130.                 sprintf('The first argument of the "%s()" method must be an instance of "%s".'__METHOD__PreAuthenticationJWTUserTokenInterface::class)
  131.             );
  132.         }
  133.         $payload $preAuthToken->getPayload();
  134.         $idClaim $this->jwtManager->getUserIdClaim();
  135.         if (!isset($payload[$idClaim])) {
  136.             throw new InvalidPayloadException($idClaim);
  137.         }
  138.         $user $this->loadUser($userProvider$payload$payload[$idClaim]);
  139.         $this->preAuthenticationTokenStorage->setToken($preAuthToken);
  140.         return $user;
  141.     }
  142.     /**
  143.      * {@inheritdoc}
  144.      */
  145.     public function onAuthenticationFailure(Request $requestAuthenticationException $authException)
  146.     {
  147.         $response = new JWTAuthenticationFailureResponse($authException->getMessageKey());
  148.         if ($authException instanceof ExpiredTokenException) {
  149.             $event = new JWTExpiredEvent($authException$response);
  150.             $eventName Events::JWT_EXPIRED;
  151.         } else {
  152.             $event = new JWTInvalidEvent($authException$response);
  153.             $eventName Events::JWT_INVALID;
  154.         }
  155.         if ($this->dispatcher instanceof ContractsEventDispatcherInterface) {
  156.             $this->dispatcher->dispatch($event$eventName);
  157.         } else {
  158.             $this->dispatcher->dispatch($eventName$event);
  159.         }
  160.         return $event->getResponse();
  161.     }
  162.     /**
  163.      * {@inheritdoc}
  164.      */
  165.     public function onAuthenticationSuccess(Request $requestTokenInterface $token$providerKey)
  166.     {
  167.         return;
  168.     }
  169.     /**
  170.      * {@inheritdoc}
  171.      *
  172.      * @return JWTAuthenticationFailureResponse
  173.      */
  174.     public function start(Request $requestAuthenticationException $authException null)
  175.     {
  176.         $exception = new MissingTokenException('JWT Token not found'0$authException);
  177.         $event     = new JWTNotFoundEvent($exception, new JWTAuthenticationFailureResponse($exception->getMessageKey()));
  178.         if ($this->dispatcher instanceof ContractsEventDispatcherInterface) {
  179.             $this->dispatcher->dispatch($eventEvents::JWT_NOT_FOUND);
  180.         } else {
  181.             $this->dispatcher->dispatch(Events::JWT_NOT_FOUND$event);
  182.         }
  183.         return $event->getResponse();
  184.     }
  185.     /**
  186.      * {@inheritdoc}
  187.      */
  188.     public function checkCredentials($credentialsUserInterface $user)
  189.     {
  190.         return true;
  191.     }
  192.     /**
  193.      * {@inheritdoc}
  194.      *
  195.      * @throws \RuntimeException If there is no pre-authenticated token previously stored
  196.      */
  197.     public function createAuthenticatedToken(UserInterface $user$providerKey)
  198.     {
  199.         $preAuthToken $this->preAuthenticationTokenStorage->getToken();
  200.         if (null === $preAuthToken) {
  201.             throw new \RuntimeException('Unable to return an authenticated token since there is no pre authentication token.');
  202.         }
  203.         $authToken = new JWTUserToken($user->getRoles(), $user$preAuthToken->getCredentials(), $providerKey);
  204.         if ($this->dispatcher instanceof ContractsEventDispatcherInterface) {
  205.             $this->dispatcher->dispatch(new JWTAuthenticatedEvent($preAuthToken->getPayload(), $authToken), Events::JWT_AUTHENTICATED);
  206.         } else {
  207.             $this->dispatcher->dispatch(Events::JWT_AUTHENTICATED, new JWTAuthenticatedEvent($preAuthToken->getPayload(), $authToken));
  208.         }
  209.         $this->preAuthenticationTokenStorage->setToken(null);
  210.         return $authToken;
  211.     }
  212.     /**
  213.      * {@inheritdoc}
  214.      */
  215.     public function supportsRememberMe()
  216.     {
  217.         return false;
  218.     }
  219.     /**
  220.      * Gets the token extractor to be used for retrieving a JWT token in the
  221.      * current request.
  222.      *
  223.      * Override this method for adding/removing extractors to the chain one or
  224.      * returning a different {@link TokenExtractorInterface} implementation.
  225.      *
  226.      * @return TokenExtractorInterface
  227.      */
  228.     protected function getTokenExtractor()
  229.     {
  230.         return $this->tokenExtractor;
  231.     }
  232.     /**
  233.      * Loads the user to authenticate.
  234.      *
  235.      * @param UserProviderInterface $userProvider An user provider
  236.      * @param array                 $payload      The token payload
  237.      * @param string                $identity     The key from which to retrieve the user "username"
  238.      *
  239.      * @return UserInterface
  240.      */
  241.     protected function loadUser(UserProviderInterface $userProvider, array $payload$identity)
  242.     {
  243.         if ($userProvider instanceof PayloadAwareUserProviderInterface) {
  244.             return $userProvider->loadUserByUsernameAndPayload($identity$payload);
  245.         }
  246.         if ($userProvider instanceof ChainUserProvider) {
  247.             foreach ($userProvider->getProviders() as $provider) {
  248.                 try {
  249.                     if ($provider instanceof PayloadAwareUserProviderInterface) {
  250.                         return $provider->loadUserByUsernameAndPayload($identity$payload);
  251.                     }
  252.                     return $provider->loadUserByUsername($identity);
  253.                 } catch (UsernameNotFoundException $e) {
  254.                     // try next one
  255.                 }
  256.             }
  257.             $ex = new UsernameNotFoundException(sprintf('There is no user with name "%s".'$identity));
  258.             $ex->setUsername($identity);
  259.             throw $ex;
  260.         }
  261.         return $userProvider->loadUserByUsername($identity);
  262.     }
  263. }