vendor/symfony/security-http/Authenticator/JsonLoginAuthenticator.php line 47

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\Authenticator;
  11. use Symfony\Component\HttpFoundation\JsonResponse;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  15. use Symfony\Component\PropertyAccess\Exception\AccessException;
  16. use Symfony\Component\PropertyAccess\PropertyAccess;
  17. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  18. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  19. use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
  20. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  21. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  22. use Symfony\Component\Security\Core\Security;
  23. use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
  24. use Symfony\Component\Security\Core\User\UserProviderInterface;
  25. use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
  26. use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
  27. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
  28. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  29. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
  30. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  31. use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
  32. use Symfony\Component\Security\Http\HttpUtils;
  33. use Symfony\Contracts\Translation\TranslatorInterface;
  34. /**
  35.  * Provides a stateless implementation of an authentication via
  36.  * a JSON document composed of a username and a password.
  37.  *
  38.  * @author Kévin Dunglas <dunglas@gmail.com>
  39.  * @author Wouter de Jong <wouter@wouterj.nl>
  40.  *
  41.  * @final
  42.  */
  43. class JsonLoginAuthenticator implements InteractiveAuthenticatorInterface
  44. {
  45.     private $options;
  46.     private $httpUtils;
  47.     private $userProvider;
  48.     private $propertyAccessor;
  49.     private $successHandler;
  50.     private $failureHandler;
  51.     /**
  52.      * @var TranslatorInterface|null
  53.      */
  54.     private $translator;
  55.     public function __construct(HttpUtils $httpUtilsUserProviderInterface $userProviderAuthenticationSuccessHandlerInterface $successHandler nullAuthenticationFailureHandlerInterface $failureHandler null, array $options = [], PropertyAccessorInterface $propertyAccessor null)
  56.     {
  57.         $this->options array_merge(['username_path' => 'username''password_path' => 'password'], $options);
  58.         $this->httpUtils $httpUtils;
  59.         $this->successHandler $successHandler;
  60.         $this->failureHandler $failureHandler;
  61.         $this->userProvider $userProvider;
  62.         $this->propertyAccessor $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
  63.     }
  64.     public function supports(Request $request): ?bool
  65.     {
  66.         $request->headers->set('Content-Type''application/json');
  67.         if (false === strpos($request->getRequestFormat() ?? '''json') && false === strpos($request->getContentType() ?? '''json')) {
  68.             return false;
  69.         }
  70.         if (isset($this->options['check_path']) && !$this->httpUtils->checkRequestPath($request$this->options['check_path'])) {
  71.             return false;
  72.         }
  73.         return true;
  74.     }
  75.     public function authenticate(Request $request): PassportInterface
  76.     {
  77.         $request->headers->set('Content-Type''application/json');
  78.         try {
  79.             $credentials $this->getCredentials($request);
  80.         } catch (BadRequestHttpException $e) {
  81.             $request->setRequestFormat('json');
  82.             throw $e;
  83.         }
  84.         // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
  85.         $method 'loadUserByIdentifier';
  86.         if (!method_exists($this->userProvider'loadUserByIdentifier')) {
  87.             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($this->userProvider));
  88.             $method 'loadUserByUsername';
  89.         }
  90.         $passport = new Passport(
  91.             new UserBadge($credentials['username'], [$this->userProvider$method]),
  92.             new PasswordCredentials($credentials['password'])
  93.         );
  94.         if ($this->userProvider instanceof PasswordUpgraderInterface) {
  95.             $passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
  96.         }
  97.         return $passport;
  98.     }
  99.     /**
  100.      * @deprecated since Symfony 5.4, use {@link createToken()} instead
  101.      */
  102.     public function createAuthenticatedToken(PassportInterface $passportstring $firewallName): TokenInterface
  103.     {
  104.         trigger_deprecation('symfony/security-http''5.4''Method "%s()" is deprecated, use "%s::createToken()" instead.'__METHOD____CLASS__);
  105.         return $this->createToken($passport$firewallName);
  106.     }
  107.     public function createToken(Passport $passportstring $firewallName): TokenInterface
  108.     {
  109.         return new UsernamePasswordToken($passport->getUser(), $firewallName$passport->getUser()->getRoles());
  110.     }
  111.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?Response
  112.     {
  113.         if (null === $this->successHandler) {
  114.             return null// let the original request continue
  115.         }
  116.         return $this->successHandler->onAuthenticationSuccess($request$token);
  117.     }
  118.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception): ?Response
  119.     {
  120.         if (null === $this->failureHandler) {
  121.             if (null !== $this->translator) {
  122.                 $errorMessage $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security');
  123.             } else {
  124.                 $errorMessage strtr($exception->getMessageKey(), $exception->getMessageData());
  125.             }
  126.             return new JsonResponse(['error' => $errorMessage], JsonResponse::HTTP_UNAUTHORIZED);
  127.         }
  128.         return $this->failureHandler->onAuthenticationFailure($request$exception);
  129.     }
  130.     public function isInteractive(): bool
  131.     {
  132.         return true;
  133.     }
  134.     public function setTranslator(TranslatorInterface $translator)
  135.     {
  136.         $this->translator $translator;
  137.     }
  138.     private function getCredentials(Request $request)
  139.     {
  140.         $request->headers->set('Content-Type''application/json');
  141.        // $data = json_decode($request->getContent());
  142.         $data json_decode(file_get_contents('php://input'));
  143.        
  144.         
  145.         if (!$data instanceof \stdClass) {
  146.             throw new BadRequestHttpException('Invalid JSON.');
  147.         }
  148.         $credentials = [];
  149.         try {
  150.             $credentials['username'] = $this->propertyAccessor->getValue($data$this->options['username_path']);
  151.             if (!\is_string($credentials['username'])) {
  152.                 throw new BadRequestHttpException(sprintf('The key "%s" must be a string.'$this->options['username_path']));
  153.             }
  154.             if (\strlen($credentials['username']) > Security::MAX_USERNAME_LENGTH) {
  155.                 throw new BadCredentialsException('Invalid username.');
  156.             }
  157.         } catch (AccessException $e) {
  158.             throw new BadRequestHttpException(sprintf('The key "%s" must be provided.'$this->options['username_path']), $e);
  159.         }
  160.         try {
  161.             $credentials['password'] = $this->propertyAccessor->getValue($data$this->options['password_path']);
  162.             if (!\is_string($credentials['password'])) {
  163.                 throw new BadRequestHttpException(sprintf('The key "%s" must be a string.'$this->options['password_path']));
  164.             }
  165.         } catch (AccessException $e) {
  166.             throw new BadRequestHttpException(sprintf('The key "%s" must be provided.'$this->options['password_path']), $e);
  167.         }
  168.         return $credentials;
  169.     }
  170. }