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

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