Skip to content

Commit

Permalink
Also use authentication failure/success handlers in FormLoginAuthenti…
Browse files Browse the repository at this point in the history
…cator
  • Loading branch information
wouterj committed Apr 20, 2020
1 parent 0fe5083 commit 9ea32c4
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 49 deletions.
Expand Up @@ -30,6 +30,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface
'check_path' => '/login_check',
'use_forward' => false,
'require_previous_session' => false,
'login_path' => '/login',
];

protected $defaultSuccessHandlerOptions = [
Expand Down
Expand Up @@ -100,12 +100,13 @@ public function createEntryPoint(ContainerBuilder $container, string $id, array
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
{
$authenticatorId = 'security.authenticator.form_login.'.$id;
$defaultOptions = array_merge($this->defaultSuccessHandlerOptions, $this->options);
$options = array_merge($defaultOptions, array_intersect_key($config, $defaultOptions));
$options = array_intersect_key($config, $this->options);
$container
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
->replaceArgument(1, new Reference($userProviderId))
->replaceArgument(2, $options);
->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config)))
->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $id, $config)))
->replaceArgument(4, $options);

return $authenticatorId;
}
Expand Down
Expand Up @@ -95,6 +95,8 @@
abstract="true">
<argument type="service" id="security.http_utils" />
<argument type="abstract">user provider</argument>
<argument type="abstract">authentication success handler</argument>
<argument type="abstract">authentication failure handler</argument>
<argument type="abstract">options</argument>
</service>

Expand Down
Expand Up @@ -30,7 +30,7 @@ abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator impl
/**
* Return the URL to the login page.
*/
abstract protected function getLoginUrl(): string;
abstract protected function getLoginUrl(Request $request): string;

/**
* Override to change what happens after a bad username/password is submitted.
Expand All @@ -41,7 +41,7 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
}

$url = $this->getLoginUrl();
$url = $this->getLoginUrl($request);

return new RedirectResponse($url);
}
Expand All @@ -52,7 +52,7 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
$url = $this->getLoginUrl();
$url = $this->getLoginUrl($request);

return new RedirectResponse($url);
}
Expand Down
Expand Up @@ -16,13 +16,15 @@
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\ParameterBagUtils;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

/**
* @author Wouter de Jong <wouter@wouterj.nl>
Expand All @@ -33,34 +35,32 @@
*/
class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements PasswordAuthenticatedInterface, CsrfProtectedAuthenticatorInterface
{
use TargetPathTrait;

private $options;
private $httpUtils;
private $userProvider;
private $successHandler;
private $failureHandler;
private $options;

public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, array $options)
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options)
{
$this->httpUtils = $httpUtils;
$this->userProvider = $userProvider;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->options = array_merge([
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'csrf_token_id' => 'authenticate',
'check_path' => '/login_check',
'post_only' => true,

'always_use_default_target_path' => false,
'default_target_path' => '/',
'login_path' => '/login',
'target_path_parameter' => '_target_path',
'use_referer' => false,
'csrf_parameter' => '_csrf_token',
'csrf_token_id' => 'authenticate',
], $options);
$this->userProvider = $userProvider;
}

protected function getLoginUrl(): string
protected function getLoginUrl(Request $request): string
{
return $this->options['login_path'];
return $this->httpUtils->generateUri($request, $this->options['login_path']);
}

public function supports(Request $request): bool
Expand Down Expand Up @@ -122,36 +122,13 @@ public function createAuthenticatedToken(UserInterface $user, $providerKey): Tok
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): Response
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
{
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request, $providerKey));
return $this->successHandler->onAuthenticationSuccess($request, $token);
}

private function determineTargetUrl(Request $request, string $providerKey)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
if ($this->options['always_use_default_target_path']) {
return $this->options['default_target_path'];
}

if ($targetUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['target_path_parameter'])) {
return $targetUrl;
}

if ($targetUrl = $this->getTargetPath($request->getSession(), $providerKey)) {
$this->removeTargetPath($request->getSession(), $providerKey);

return $targetUrl;
}

if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) {
if (false !== $pos = strpos($targetUrl, '?')) {
$targetUrl = substr($targetUrl, 0, $pos);
}
if ($targetUrl && $targetUrl !== $this->httpUtils->generateUri($request, $this->options['login_path'])) {
return $targetUrl;
}
}

return $this->options['default_target_path'];
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
}
Expand Up @@ -17,17 +17,23 @@
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
use Symfony\Component\Security\Http\HttpUtils;

class FormLoginAuthenticatorTest extends TestCase
{
private $userProvider;
private $successHandler;
private $failureHandler;
private $authenticator;

protected function setUp(): void
{
$this->userProvider = $this->createMock(UserProviderInterface::class);
$this->successHandler = $this->createMock(AuthenticationSuccessHandlerInterface::class);
$this->failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
}

/**
Expand Down Expand Up @@ -123,7 +129,7 @@ public function postOnlyDataProvider()

private function setUpAuthenticator(array $options = [])
{
$this->authenticator = new FormLoginAuthenticator(new HttpUtils(), $this->userProvider, $options);
$this->authenticator = new FormLoginAuthenticator(new HttpUtils(), $this->userProvider, $this->successHandler, $this->failureHandler, $options);
}

private function createSession()
Expand Down

0 comments on commit 9ea32c4

Please sign in to comment.