vendor/symfony/security-http/Authenticator/FormLoginAuthenticator.php line 43

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\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  14. use Symfony\Component\HttpKernel\HttpKernelInterface;
  15. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  16. use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
  17. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  18. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  19. use Symfony\Component\Security\Core\Security;
  20. use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
  21. use Symfony\Component\Security\Core\User\UserProviderInterface;
  22. use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
  23. use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
  24. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
  25. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
  26. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
  27. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  28. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
  29. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  30. use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
  31. use Symfony\Component\Security\Http\HttpUtils;
  32. use Symfony\Component\Security\Http\ParameterBagUtils;
  33. /**
  34. * @author Wouter de Jong <wouter@wouterj.nl>
  35. * @author Fabien Potencier <fabien@symfony.com>
  36. *
  37. * @final
  38. */
  39. class FormLoginAuthenticator extends AbstractLoginFormAuthenticator
  40. {
  41. private $httpUtils;
  42. private $userProvider;
  43. private $successHandler;
  44. private $failureHandler;
  45. private $options;
  46. private $httpKernel;
  47. public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options)
  48. {
  49. $this->httpUtils = $httpUtils;
  50. $this->userProvider = $userProvider;
  51. $this->successHandler = $successHandler;
  52. $this->failureHandler = $failureHandler;
  53. $this->options = array_merge([
  54. 'username_parameter' => '_username',
  55. 'password_parameter' => '_password',
  56. 'check_path' => '/login_check',
  57. 'post_only' => true,
  58. 'form_only' => false,
  59. 'enable_csrf' => false,
  60. 'csrf_parameter' => '_csrf_token',
  61. 'csrf_token_id' => 'authenticate',
  62. ], $options);
  63. }
  64. protected function getLoginUrl(Request $request): string
  65. {
  66. return $this->httpUtils->generateUri($request, $this->options['login_path']);
  67. }
  68. public function supports(Request $request): bool
  69. {
  70. return ($this->options['post_only'] ? $request->isMethod('POST') : true)
  71. && $this->httpUtils->checkRequestPath($request, $this->options['check_path'])
  72. && ($this->options['form_only'] ? 'form' === $request->getContentType() : true);
  73. }
  74. public function authenticate(Request $request): Passport
  75. {
  76. $credentials = $this->getCredentials($request);
  77. // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
  78. $method = 'loadUserByIdentifier';
  79. if (!method_exists($this->userProvider, 'loadUserByIdentifier')) {
  80. 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));
  81. $method = 'loadUserByUsername';
  82. }
  83. $passport = new Passport(
  84. new UserBadge($credentials['username'], [$this->userProvider, $method]),
  85. new PasswordCredentials($credentials['password']),
  86. [new RememberMeBadge()]
  87. );
  88. if ($this->options['enable_csrf']) {
  89. $passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token']));
  90. }
  91. if ($this->userProvider instanceof PasswordUpgraderInterface) {
  92. $passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
  93. }
  94. return $passport;
  95. }
  96. /**
  97. * @deprecated since Symfony 5.4, use {@link createToken()} instead
  98. */
  99. public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
  100. {
  101. trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__);
  102. return $this->createToken($passport, $firewallName);
  103. }
  104. public function createToken(Passport $passport, string $firewallName): TokenInterface
  105. {
  106. return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
  107. }
  108. public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
  109. {
  110. return $this->successHandler->onAuthenticationSuccess($request, $token);
  111. }
  112. public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
  113. {
  114. return $this->failureHandler->onAuthenticationFailure($request, $exception);
  115. }
  116. private function getCredentials(Request $request): array
  117. {
  118. $credentials = [];
  119. $credentials['csrf_token'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']);
  120. if ($this->options['post_only']) {
  121. $credentials['username'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']);
  122. $credentials['password'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']) ?? '';
  123. } else {
  124. $credentials['username'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']);
  125. $credentials['password'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']) ?? '';
  126. }
  127. if (!\is_string($credentials['username']) && (!\is_object($credentials['username']) || !method_exists($credentials['username'], '__toString'))) {
  128. throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($credentials['username'])));
  129. }
  130. $credentials['username'] = trim($credentials['username']);
  131. if (\strlen($credentials['username']) > Security::MAX_USERNAME_LENGTH) {
  132. throw new BadCredentialsException('Invalid username.');
  133. }
  134. $request->getSession()->set(Security::LAST_USERNAME, $credentials['username']);
  135. if (!\is_string($credentials['password']) && (!\is_object($credentials['password']) || !method_exists($credentials['password'], '__toString'))) {
  136. throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['password_parameter'], \gettype($credentials['password'])));
  137. }
  138. if (!\is_string($credentials['csrf_token'] ?? '') && (!\is_object($credentials['csrf_token']) || !method_exists($credentials['csrf_token'], '__toString'))) {
  139. throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['csrf_parameter'], \gettype($credentials['csrf_token'])));
  140. }
  141. return $credentials;
  142. }
  143. public function setHttpKernel(HttpKernelInterface $httpKernel): void
  144. {
  145. $this->httpKernel = $httpKernel;
  146. }
  147. public function start(Request $request, ?AuthenticationException $authException = null): Response
  148. {
  149. if (!$this->options['use_forward']) {
  150. return parent::start($request, $authException);
  151. }
  152. $subRequest = $this->httpUtils->createRequest($request, $this->options['login_path']);
  153. $response = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
  154. if (200 === $response->getStatusCode()) {
  155. $response->setStatusCode(401);
  156. }
  157. return $response;
  158. }
  159. }