vendor/symfony/security-http/RememberMe/PersistentTokenBasedRememberMeServices.php line 24

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Http\RememberMe;
  11. use Symfony\Component\HttpFoundation\Cookie;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
  15. use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface;
  16. use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface;
  17. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  18. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  19. use Symfony\Component\Security\Core\Exception\CookieTheftException;
  20. trigger_deprecation('symfony/security-http''5.4''The "%s" class is deprecated, use "%s" instead.'PersistentTokenBasedRememberMeServices::class, PersistentRememberMeHandler::class);
  21. /**
  22.  * Concrete implementation of the RememberMeServicesInterface which needs
  23.  * an implementation of TokenProviderInterface for providing remember-me
  24.  * capabilities.
  25.  *
  26.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  27.  *
  28.  * @deprecated since Symfony 5.4, use {@see PersistentRememberMeHandler} instead
  29.  */
  30. class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
  31. {
  32.     private const HASHED_TOKEN_PREFIX 'sha256_';
  33.     /** @var TokenProviderInterface */
  34.     private $tokenProvider;
  35.     public function setTokenProvider(TokenProviderInterface $tokenProvider)
  36.     {
  37.         $this->tokenProvider $tokenProvider;
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      */
  42.     protected function cancelCookie(Request $request)
  43.     {
  44.         // Delete cookie on the client
  45.         parent::cancelCookie($request);
  46.         // Delete cookie from the tokenProvider
  47.         if (null !== ($cookie $request->cookies->get($this->options['name']))
  48.             && === \count($parts $this->decodeCookie($cookie))
  49.         ) {
  50.             [$series] = $parts;
  51.             $this->tokenProvider->deleteTokenBySeries($series);
  52.         }
  53.     }
  54.     /**
  55.      * {@inheritdoc}
  56.      */
  57.     protected function processAutoLoginCookie(array $cookiePartsRequest $request)
  58.     {
  59.         if (!== \count($cookieParts)) {
  60.             throw new AuthenticationException('The cookie is invalid.');
  61.         }
  62.         [$series$tokenValue] = $cookieParts;
  63.         $persistentToken $this->tokenProvider->loadTokenBySeries($series);
  64.         if (!$this->isTokenValueValid($persistentToken$tokenValue)) {
  65.             throw new CookieTheftException('This token was already used. The account is possibly compromised.');
  66.         }
  67.         if ($persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'] < time()) {
  68.             throw new AuthenticationException('The cookie has expired.');
  69.         }
  70.         $tokenValue base64_encode(random_bytes(64));
  71.         $this->tokenProvider->updateToken($series$this->generateHash($tokenValue), new \DateTime());
  72.         $request->attributes->set(self::COOKIE_ATTR_NAME,
  73.             new Cookie(
  74.                 $this->options['name'],
  75.                 $this->encodeCookie([$series$tokenValue]),
  76.                 time() + $this->options['lifetime'],
  77.                 $this->options['path'],
  78.                 $this->options['domain'],
  79.                 $this->options['secure'] ?? $request->isSecure(),
  80.                 $this->options['httponly'],
  81.                 false,
  82.                 $this->options['samesite']
  83.             )
  84.         );
  85.         $userProvider $this->getUserProvider($persistentToken->getClass());
  86.         // @deprecated since Symfony 5.3, change to $persistentToken->getUserIdentifier() in 6.0
  87.         if (method_exists($persistentToken'getUserIdentifier')) {
  88.             $userIdentifier $persistentToken->getUserIdentifier();
  89.         } else {
  90.             trigger_deprecation('symfony/security-core''5.3''Not implementing method "getUserIdentifier()" in persistent token "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.'get_debug_type($persistentToken));
  91.             $userIdentifier $persistentToken->getUsername();
  92.         }
  93.         // @deprecated since Symfony 5.3, change to $userProvider->loadUserByIdentifier() in 6.0
  94.         if (method_exists($userProvider'loadUserByIdentifier')) {
  95.             return $userProvider->loadUserByIdentifier($userIdentifier);
  96.         } else {
  97.             trigger_deprecation('symfony/security-core''5.3''Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.'get_debug_type($userProvider));
  98.             return $userProvider->loadUserByUsername($userIdentifier);
  99.         }
  100.     }
  101.     /**
  102.      * {@inheritdoc}
  103.      */
  104.     protected function onLoginSuccess(Request $requestResponse $responseTokenInterface $token)
  105.     {
  106.         $series base64_encode(random_bytes(64));
  107.         $tokenValue base64_encode(random_bytes(64));
  108.         $this->tokenProvider->createNewToken(
  109.             new PersistentToken(
  110.                 \get_class($user $token->getUser()),
  111.                 // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0
  112.                 method_exists($user'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(),
  113.                 $series,
  114.                 $this->generateHash($tokenValue),
  115.                 new \DateTime()
  116.             )
  117.         );
  118.         $response->headers->setCookie(
  119.             new Cookie(
  120.                 $this->options['name'],
  121.                 $this->encodeCookie([$series$tokenValue]),
  122.                 time() + $this->options['lifetime'],
  123.                 $this->options['path'],
  124.                 $this->options['domain'],
  125.                 $this->options['secure'] ?? $request->isSecure(),
  126.                 $this->options['httponly'],
  127.                 false,
  128.                 $this->options['samesite']
  129.             )
  130.         );
  131.     }
  132.     private function generateHash(string $tokenValue): string
  133.     {
  134.         return self::HASHED_TOKEN_PREFIX.hash_hmac('sha256'$tokenValue$this->getSecret());
  135.     }
  136.     private function isTokenValueValid(PersistentTokenInterface $persistentTokenstring $tokenValue): bool
  137.     {
  138.         if (=== strpos($persistentToken->getTokenValue(), self::HASHED_TOKEN_PREFIX)) {
  139.             return hash_equals($persistentToken->getTokenValue(), $this->generateHash($tokenValue));
  140.         }
  141.         return hash_equals($persistentToken->getTokenValue(), $tokenValue);
  142.     }
  143. }