Refactor to an event based authentication approach
This allows more flexibility for the authentication manager (to e.g. implement login throttling, easier remember me, etc). It is also a known design pattern in Symfony HttpKernel.
This commit is contained in:
parent
b14a5e8c52
commit
999ec2795f
@ -106,7 +106,7 @@ class FormLoginFactory extends AbstractFactory implements GuardFactoryInterface,
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
|
||||
->replaceArgument(1, isset($config['csrf_token_generator']) ? new Reference($config['csrf_token_generator']) : null)
|
||||
->replaceArgument(2, new Reference($userProviderId))
|
||||
->replaceArgument(4, $options);
|
||||
->replaceArgument(3, $options);
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
@ -419,16 +419,13 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
$configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null;
|
||||
|
||||
if ($this->guardAuthenticationManagerEnabled) {
|
||||
// guard authentication manager listener (must be before calling createAuthenticationListeners() to inject remember me services)
|
||||
// Remember me listener (must be before calling createAuthenticationListeners() to inject remember me services)
|
||||
$container
|
||||
->setDefinition('security.firewall.guard.'.$id, new ChildDefinition('security.firewall.guard'))
|
||||
->replaceArgument(2, new Reference('security.firewall.guard.'.$id.'.locator'))
|
||||
->replaceArgument(3, $id)
|
||||
->addTag('kernel.event_listener', ['event' => KernelEvents::REQUEST])
|
||||
->setDefinition('security.listener.remember_me.'.$id, new ChildDefinition('security.listener.remember_me'))
|
||||
->replaceArgument(0, $id)
|
||||
->addTag('kernel.event_subscriber')
|
||||
->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none'])
|
||||
;
|
||||
|
||||
$listeners[] = new Reference('security.firewall.guard.'.$id);
|
||||
}
|
||||
|
||||
// Authentication listeners
|
||||
@ -438,7 +435,7 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
$authenticationProviders = array_merge($authenticationProviders, $firewallAuthenticationProviders);
|
||||
|
||||
if ($this->guardAuthenticationManagerEnabled) {
|
||||
// add authentication providers for this firewall to the GuardManagerListener (if guard is enabled)
|
||||
// guard authentication manager listener
|
||||
$container
|
||||
->setDefinition('security.firewall.guard.'.$id.'.locator', new ChildDefinition('security.firewall.guard.locator'))
|
||||
->setArguments([array_map(function ($id) {
|
||||
@ -446,10 +443,15 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
}, $firewallAuthenticationProviders)])
|
||||
->addTag('container.service_locator')
|
||||
;
|
||||
|
||||
$container
|
||||
->getDefinition('security.firewall.guard.'.$id)
|
||||
->setDefinition('security.firewall.guard.'.$id, new ChildDefinition('security.firewall.guard'))
|
||||
->replaceArgument(2, new Reference('security.firewall.guard.'.$id.'.locator'))
|
||||
->replaceArgument(3, $id)
|
||||
->addTag('kernel.event_listener', ['event' => KernelEvents::REQUEST])
|
||||
;
|
||||
|
||||
$listeners[] = new Reference('security.firewall.guard.'.$id);
|
||||
}
|
||||
|
||||
$config->replaceArgument(7, $configuredEntryPoint ?: $defaultEntryPoint);
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Bundle\SecurityBundle\EventListener;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||
@ -32,9 +33,10 @@ class LazyGuardManagerListener extends GuardManagerListener
|
||||
GuardAuthenticatorHandler $guardHandler,
|
||||
ServiceLocator $guardLocator,
|
||||
string $providerKey,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($authenticationManager, $guardHandler, [], $providerKey, $logger);
|
||||
parent::__construct($authenticationManager, $guardHandler, [], $providerKey, $eventDispatcher, $logger);
|
||||
|
||||
$this->guardLocator = $guardLocator;
|
||||
}
|
||||
|
@ -16,9 +16,37 @@
|
||||
<argument type="service" id="security.authentication.guard_handler" />
|
||||
<argument/> <!-- guard authenticator locator -->
|
||||
<argument/> <!-- provider key -->
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<!-- Listeners -->
|
||||
|
||||
<service id="Symfony\Component\Security\Http\EventListener\AuthenticatingListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="security.encoder_factory" />
|
||||
</service>
|
||||
|
||||
<service id="Symfony\Component\Security\Http\EventListener\PasswordMigratingListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="security.encoder_factory" />
|
||||
</service>
|
||||
|
||||
<service id="Symfony\Component\Security\Http\EventListener\UserCheckerListener">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="Symfony\Component\Security\Core\User\UserCheckerInterface" />
|
||||
</service>
|
||||
|
||||
<service id="security.listener.remember_me"
|
||||
class="Symfony\Component\Security\Http\EventListener\RememberMeListener"
|
||||
abstract="true">
|
||||
<tag name="monolog.logger" channel="security" />
|
||||
<argument/> <!-- provider key -->
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<!-- Authenticators -->
|
||||
|
||||
<service id="security.authenticator.http_basic"
|
||||
class="Symfony\Component\Security\Http\Authentication\Authenticator\HttpBasicAuthenticator"
|
||||
abstract="true">
|
||||
@ -34,14 +62,13 @@
|
||||
<argument type="service" id="security.http_utils" />
|
||||
<argument /> <!-- csrf token generator -->
|
||||
<argument type="abstract">user provider</argument>
|
||||
<argument type="service" id="security.encoder_factory" />
|
||||
<argument type="abstract">options</argument>
|
||||
</service>
|
||||
|
||||
<service id="security.authenticator.anonymous"
|
||||
class="Symfony\Component\Security\Http\Authentication\Authenticator\AnonymousAuthenticator"
|
||||
abstract="true">
|
||||
<argument /> <!-- secret -->
|
||||
<argument type="abstract">secret</argument>
|
||||
<argument type="service" id="security.token_storage" />
|
||||
</service>
|
||||
</services>
|
||||
|
@ -54,7 +54,7 @@
|
||||
</service>
|
||||
<service id="security.authentication.manager.guard" class="Symfony\Component\Security\Http\Authentication\GuardAuthenticationManager">
|
||||
<argument /> <!-- guard authenticators -->
|
||||
<argument type="service" id="Symfony\Component\Security\Core\User\UserCheckerInterface" /> <!-- User Checker -->
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
<argument>%security.authentication.manager.erase_credentials%</argument>
|
||||
<call method="setEventDispatcher">
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
|
@ -13,9 +13,12 @@ namespace Symfony\Component\Security\Guard\Firewall;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
||||
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken as GuardPreAuthenticationGuardToken;
|
||||
@ -104,15 +107,122 @@ class GuardAuthenticationListener extends AbstractListener
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AuthenticatorInterface[] $guardAuthenticators
|
||||
*/
|
||||
protected function executeGuardAuthenticators(array $guardAuthenticators, RequestEvent $event): void
|
||||
{
|
||||
foreach ($guardAuthenticators as $key => $guardAuthenticator) {
|
||||
$uniqueGuardKey = $this->providerKey.'_'.$key;;
|
||||
|
||||
$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function executeGuardAuthenticator(string $uniqueGuardKey, AuthenticatorInterface $guardAuthenticator, RequestEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
try {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
// allow the authenticator to fetch authentication info from the request
|
||||
$credentials = $guardAuthenticator->getCredentials($request);
|
||||
|
||||
if (null === $credentials) {
|
||||
throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
// create a token with the unique key, so that the provider knows which authenticator to use
|
||||
$token = $this->createPreAuthenticatedToken($credentials, $uniqueGuardKey, $this->providerKey);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
// pass the token into the AuthenticationManager system
|
||||
// this indirectly calls GuardAuthenticationProvider::authenticate()
|
||||
$token = $this->authenticationManager->authenticate($token);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Guard authentication successful!', ['token' => $token, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
// sets the token on the token storage, etc
|
||||
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
|
||||
} catch (AuthenticationException $e) {
|
||||
// oh no! Authentication failed!
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Guard authentication failed.', ['exception' => $e, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
|
||||
|
||||
if ($response instanceof Response) {
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// success!
|
||||
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
|
||||
if ($response instanceof Response) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Guard authenticator set success response.', ['response' => $response, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to trigger the remember me functionality
|
||||
$this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
|
||||
}
|
||||
|
||||
protected function triggerRememberMe($guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
|
||||
{
|
||||
if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof CoreAuthenticatorInterface) {
|
||||
throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.');
|
||||
}
|
||||
|
||||
if (null === $this->rememberMeServices) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$guardAuthenticator->supportsRememberMe()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
throw new \LogicException(sprintf('"%s::onAuthenticationSuccess()" *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
$this->rememberMeServices->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken
|
||||
{
|
||||
return new GuardPreAuthenticationGuardToken($credentials, $uniqueGuardKey, $providerKey);
|
||||
}
|
||||
|
||||
protected function getGuardKey(string $key): string
|
||||
{
|
||||
// get a key that's unique to *this* guard authenticator
|
||||
// this MUST be the same as GuardAuthenticationProvider
|
||||
return $this->providerKey.'_'.$key;
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
namespace Symfony\Component\Security\Guard;
|
||||
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
|
||||
/**
|
||||
* An optional interface for "guard" authenticators that deal with user passwords.
|
||||
*/
|
||||
@ -24,6 +22,4 @@ interface PasswordAuthenticatedInterface
|
||||
* @param mixed $credentials The user credentials
|
||||
*/
|
||||
public function getPassword($credentials): ?string;
|
||||
|
||||
/* public function getPasswordEncoder(): ?UserPasswordEncoderInterface; */
|
||||
}
|
||||
|
@ -11,6 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\Security\Guard\Provider;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\GuardAuthenticationManagerTrait;
|
||||
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
@ -22,6 +30,7 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Guard\AuthenticatorInterface;
|
||||
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
|
||||
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
/**
|
||||
* Responsible for accepting the PreAuthenticationGuardToken and calling
|
||||
@ -41,6 +50,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
||||
private $providerKey;
|
||||
private $userChecker;
|
||||
private $passwordEncoder;
|
||||
private $rememberMeServices;
|
||||
|
||||
/**
|
||||
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
|
||||
@ -106,8 +116,48 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
||||
return $token instanceof GuardTokenInterface;
|
||||
}
|
||||
|
||||
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
|
||||
{
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
}
|
||||
|
||||
protected function getGuardKey(string $key): string
|
||||
{
|
||||
return $this->providerKey.'_'.$key;
|
||||
}
|
||||
|
||||
private function authenticateViaGuard(AuthenticatorInterface $guardAuthenticator, \Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken $token, string $providerKey): TokenInterface
|
||||
{
|
||||
// get the user from the GuardAuthenticator
|
||||
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
|
||||
if (null === $user) {
|
||||
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
if (!$user instanceof UserInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($user)));
|
||||
}
|
||||
|
||||
$this->userChecker->checkPreAuth($user);
|
||||
if (true !== $checkCredentialsResult = $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) {
|
||||
if (false !== $checkCredentialsResult) {
|
||||
throw new \TypeError(sprintf('"%s::checkCredentials()" must return a boolean value.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
throw new BadCredentialsException(sprintf('Authentication failed because "%s::checkCredentials()" did not return true.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
if ($this->userProvider instanceof PasswordUpgraderInterface && $guardAuthenticator instanceof PasswordAuthenticatedInterface && null !== $this->passwordEncoder && (null !== $password = $guardAuthenticator->getPassword($token->getCredentials())) && method_exists($this->passwordEncoder, 'needsRehash') && $this->passwordEncoder->needsRehash($user)) {
|
||||
$this->userProvider->upgradePassword($user, $this->passwordEncoder->encodePassword($user, $password));
|
||||
}
|
||||
$this->userChecker->checkPostAuth($user);
|
||||
|
||||
// turn the UserInterface into a TokenInterface
|
||||
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $providerKey);
|
||||
if (!$authenticatedToken instanceof TokenInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The "%s::createAuthenticatedToken()" method must return a TokenInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($authenticatedToken)));
|
||||
}
|
||||
|
||||
return $authenticatedToken;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class AnonymousAuthenticator implements AuthenticatorInterface
|
||||
class AnonymousAuthenticator implements AuthenticatorInterface, CustomAuthenticatedInterface
|
||||
{
|
||||
private $secret;
|
||||
private $tokenStorage;
|
||||
@ -49,16 +49,17 @@ class AnonymousAuthenticator implements AuthenticatorInterface
|
||||
return [];
|
||||
}
|
||||
|
||||
public function checkCredentials($credentials, UserInterface $user): bool
|
||||
{
|
||||
// anonymous users do not have credentials
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getUser($credentials): ?UserInterface
|
||||
{
|
||||
return new User('anon.', null);
|
||||
}
|
||||
|
||||
public function checkCredentials($credentials, UserInterface $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createAuthenticatedToken(UserInterface $user, string $providerKey): TokenInterface
|
||||
{
|
||||
return new AnonymousToken($this->secret, 'anon.', []);
|
||||
|
@ -70,18 +70,6 @@ interface AuthenticatorInterface
|
||||
*/
|
||||
public function getUser($credentials): ?UserInterface;
|
||||
|
||||
/**
|
||||
* Returns true if the credentials are valid.
|
||||
*
|
||||
* If false is returned, authentication will fail. You may also throw
|
||||
* an AuthenticationException if you wish to cause authentication to fail.
|
||||
*
|
||||
* @param mixed $credentials the value returned from getCredentials()
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function checkCredentials($credentials, UserInterface $user): bool;
|
||||
|
||||
/**
|
||||
* Create an authenticated token for the given user.
|
||||
*
|
||||
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\Authentication\Authenticator;
|
||||
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* This interface should be implemented by authenticators that
|
||||
* require custom (not password related) authentication.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface CustomAuthenticatedInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if the credentials are valid.
|
||||
*
|
||||
* If false is returned, authentication will fail. You may also throw
|
||||
* an AuthenticationException if you wish to cause authentication to fail.
|
||||
*
|
||||
* @param mixed $credentials the value returned from getCredentials()
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function checkCredentials($credentials, UserInterface $user): bool;
|
||||
}
|
@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
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\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
|
||||
@ -23,6 +24,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\HttpUtils;
|
||||
use Symfony\Component\Security\Http\ParameterBagUtils;
|
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
@ -34,23 +36,19 @@ use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
|
||||
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
|
||||
{
|
||||
use TargetPathTrait, UsernamePasswordTrait {
|
||||
UsernamePasswordTrait::checkCredentials as checkPassword;
|
||||
}
|
||||
use TargetPathTrait;
|
||||
|
||||
private $options;
|
||||
private $httpUtils;
|
||||
private $csrfTokenManager;
|
||||
private $userProvider;
|
||||
private $encoderFactory;
|
||||
|
||||
public function __construct(HttpUtils $httpUtils, ?CsrfTokenManagerInterface $csrfTokenManager, UserProviderInterface $userProvider, EncoderFactoryInterface $encoderFactory, array $options)
|
||||
public function __construct(HttpUtils $httpUtils, ?CsrfTokenManagerInterface $csrfTokenManager, UserProviderInterface $userProvider, array $options)
|
||||
{
|
||||
$this->httpUtils = $httpUtils;
|
||||
$this->csrfTokenManager = $csrfTokenManager;
|
||||
$this->encoderFactory = $encoderFactory;
|
||||
$this->options = array_merge([
|
||||
'username_parameter' => '_username',
|
||||
'password_parameter' => '_password',
|
||||
@ -109,11 +107,17 @@ class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
public function getPassword($credentials): ?string
|
||||
{
|
||||
return $credentials['password'];
|
||||
}
|
||||
|
||||
public function getUser($credentials): ?UserInterface
|
||||
{
|
||||
return $this->userProvider->loadUserByUsername($credentials['username']);
|
||||
}
|
||||
|
||||
/* @todo How to do CSRF protection?
|
||||
public function checkCredentials($credentials, UserInterface $user): bool
|
||||
{
|
||||
if (null !== $this->csrfTokenManager) {
|
||||
@ -123,6 +127,11 @@ class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
|
||||
}
|
||||
|
||||
return $this->checkPassword($credentials, $user);
|
||||
}*/
|
||||
|
||||
public function createAuthenticatedToken(UserInterface $user, $providerKey): TokenInterface
|
||||
{
|
||||
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): Response
|
||||
|
@ -15,10 +15,12 @@ use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
||||
|
||||
/**
|
||||
@ -28,10 +30,8 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface
|
||||
class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface, PasswordAuthenticatedInterface
|
||||
{
|
||||
use UsernamePasswordTrait;
|
||||
|
||||
private $realmName;
|
||||
private $userProvider;
|
||||
private $encoderFactory;
|
||||
@ -67,11 +67,21 @@ class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEn
|
||||
];
|
||||
}
|
||||
|
||||
public function getPassword($credentials): ?string
|
||||
{
|
||||
return $credentials['password'];
|
||||
}
|
||||
|
||||
public function getUser($credentials): ?UserInterface
|
||||
{
|
||||
return $this->userProvider->loadUserByUsername($credentials['username']);
|
||||
}
|
||||
|
||||
public function createAuthenticatedToken(UserInterface $user, $providerKey): TokenInterface
|
||||
{
|
||||
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response
|
||||
{
|
||||
return null;
|
||||
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\Authentication\Authenticator;
|
||||
|
||||
/**
|
||||
* This interface should be implemented when the authenticator
|
||||
* doesn't need to check credentials (e.g. when using API tokens)
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface TokenAuthenticatedInterface
|
||||
{
|
||||
/**
|
||||
* Extracts the token from the credentials.
|
||||
*
|
||||
* If you return null, the credentials will not be marked as
|
||||
* valid and a BadCredentialsException is thrown.
|
||||
*
|
||||
* @param mixed $credentials The user credentials
|
||||
*
|
||||
* @return mixed|null the token - if any - or null otherwise
|
||||
*/
|
||||
public function getToken($credentials);
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Security\Http\Authentication\Authenticator;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @property EncoderFactoryInterface $encoderFactory
|
||||
*
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
trait UsernamePasswordTrait
|
||||
{
|
||||
public function checkCredentials($credentials, UserInterface $user): bool
|
||||
{
|
||||
if (!$this->encoderFactory instanceof EncoderFactoryInterface) {
|
||||
throw new \LogicException(\get_class($this).' uses the '.__CLASS__.' trait, which requires an $encoderFactory property to be initialized with an '.EncoderFactoryInterface::class.' implementation.');
|
||||
}
|
||||
|
||||
if ('' === $credentials['password']) {
|
||||
throw new BadCredentialsException('The presented password cannot be empty.');
|
||||
}
|
||||
|
||||
if (!$this->encoderFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $credentials['password'], null)) {
|
||||
throw new BadCredentialsException('The presented password is invalid.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createAuthenticatedToken(UserInterface $user, $providerKey): TokenInterface
|
||||
{
|
||||
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
|
||||
}
|
||||
}
|
@ -12,6 +12,9 @@
|
||||
namespace Symfony\Component\Security\Http\Authentication;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
@ -21,7 +24,7 @@ use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
|
||||
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
@ -35,18 +38,16 @@ class GuardAuthenticationManager implements AuthenticationManagerInterface
|
||||
use GuardAuthenticationManagerTrait;
|
||||
|
||||
private $guardAuthenticators;
|
||||
private $userChecker;
|
||||
private $eraseCredentials;
|
||||
/** @var EventDispatcherInterface */
|
||||
private $eventDispatcher;
|
||||
private $eraseCredentials;
|
||||
|
||||
/**
|
||||
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
|
||||
*/
|
||||
public function __construct($guardAuthenticators, UserCheckerInterface $userChecker, bool $eraseCredentials = true)
|
||||
public function __construct(iterable $guardAuthenticators, EventDispatcherInterface $eventDispatcher, bool $eraseCredentials = true)
|
||||
{
|
||||
$this->guardAuthenticators = $guardAuthenticators;
|
||||
$this->userChecker = $userChecker;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->eraseCredentials = $eraseCredentials;
|
||||
}
|
||||
|
||||
@ -100,6 +101,40 @@ class GuardAuthenticationManager implements AuthenticationManagerInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getGuardKey(string $key): string
|
||||
{
|
||||
// Guard authenticators in the GuardAuthenticationManager are already indexed
|
||||
// by an unique key
|
||||
return $key;
|
||||
}
|
||||
|
||||
private function authenticateViaGuard(AuthenticatorInterface $authenticator, PreAuthenticationGuardToken $token, string $providerKey): TokenInterface
|
||||
{
|
||||
// get the user from the Authenticator
|
||||
$user = $authenticator->getUser($token->getCredentials());
|
||||
if (null === $user) {
|
||||
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', \get_class($authenticator)));
|
||||
}
|
||||
|
||||
if (!$user instanceof UserInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', \get_class($authenticator), \is_object($user) ? \get_class($user) : \gettype($user)));
|
||||
}
|
||||
|
||||
$event = new VerifyAuthenticatorCredentialsEvent($authenticator, $token, $user);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
if (true !== $event->areCredentialsValid()) {
|
||||
throw new BadCredentialsException(sprintf('Authentication failed because %s did not approve the credentials.', \get_class($authenticator)));
|
||||
}
|
||||
|
||||
// turn the UserInterface into a TokenInterface
|
||||
$authenticatedToken = $authenticator->createAuthenticatedToken($user, $providerKey);
|
||||
if (!$authenticatedToken instanceof TokenInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', \get_class($authenticator), \is_object($authenticatedToken) ? \get_class($authenticatedToken) : \gettype($authenticatedToken)));
|
||||
}
|
||||
|
||||
return $authenticatedToken;
|
||||
}
|
||||
|
||||
private function handleFailure(AuthenticationException $exception, TokenInterface $token)
|
||||
{
|
||||
if (null !== $this->eventDispatcher) {
|
||||
@ -110,11 +145,4 @@ class GuardAuthenticationManager implements AuthenticationManagerInterface
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
protected function getGuardKey(string $key): string
|
||||
{
|
||||
// Guard authenticators in the GuardAuthenticationManager are already indexed
|
||||
// by an unique key
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
@ -29,65 +29,6 @@ use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
*/
|
||||
trait GuardAuthenticationManagerTrait
|
||||
{
|
||||
/**
|
||||
* @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator
|
||||
*/
|
||||
private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token, string $providerKey): TokenInterface
|
||||
{
|
||||
// get the user from the GuardAuthenticator
|
||||
if ($guardAuthenticator instanceof AuthenticatorInterface) {
|
||||
if (!isset($this->userProvider)) {
|
||||
throw new LogicException(sprintf('%s only supports authenticators implementing "%s", update "%s" or use the legacy guard integration instead.', __CLASS__, CoreAuthenticatorInterface::class, \get_class($guardAuthenticator)));
|
||||
}
|
||||
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
|
||||
} elseif ($guardAuthenticator instanceof CoreAuthenticatorInterface) {
|
||||
$user = $guardAuthenticator->getUser($token->getCredentials());
|
||||
} else {
|
||||
throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.');
|
||||
}
|
||||
|
||||
if (null === $user) {
|
||||
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
if (!$user instanceof UserInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($user)));
|
||||
}
|
||||
|
||||
$this->userChecker->checkPreAuth($user);
|
||||
if (true !== $checkCredentialsResult = $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) {
|
||||
if (false !== $checkCredentialsResult) {
|
||||
throw new \TypeError(sprintf('"%s::checkCredentials()" must return a boolean value.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
throw new BadCredentialsException(sprintf('Authentication failed because "%s::checkCredentials()" did not return true.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
if ($guardAuthenticator instanceof PasswordAuthenticatedInterface
|
||||
&& null !== $password = $guardAuthenticator->getPassword($token->getCredentials())
|
||||
&& null !== $passwordEncoder = $this->passwordEncoder ?? (method_exists($guardAuthenticator, 'getPasswordEncoder') ? $guardAuthenticator->getPasswordEncoder() : null)
|
||||
) {
|
||||
if (method_exists($passwordEncoder, 'needsRehash') && $passwordEncoder->needsRehash($user)) {
|
||||
if (!isset($this->userProvider)) {
|
||||
if ($guardAuthenticator instanceof PasswordUpgraderInterface) {
|
||||
$guardAuthenticator->upgradePassword($user, $guardAuthenticator->getPasswordEncoder()->encodePassword($user, $password));
|
||||
}
|
||||
} elseif ($this->userProvider instanceof PasswordUpgraderInterface) {
|
||||
$this->userProvider->upgradePassword($user, $passwordEncoder->encodePassword($user, $password));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->userChecker->checkPostAuth($user);
|
||||
|
||||
// turn the UserInterface into a TokenInterface
|
||||
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $providerKey);
|
||||
if (!$authenticatedToken instanceof TokenInterface) {
|
||||
throw new \UnexpectedValueException(sprintf('The "%s::createAuthenticatedToken()" method must return a TokenInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($authenticatedToken)));
|
||||
}
|
||||
|
||||
return $authenticatedToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CoreAuthenticatorInterface|\Symfony\Component\Security\Guard\AuthenticatorInterface|null
|
||||
*/
|
||||
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\Event;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* This event is dispatched after an error during authentication.
|
||||
*
|
||||
* Listeners to this event can change state based on authentication
|
||||
* failure (e.g. to implement login throttling).
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class LoginFailureEvent extends Event
|
||||
{
|
||||
private $exception;
|
||||
private $authenticator;
|
||||
private $request;
|
||||
private $response;
|
||||
private $providerKey;
|
||||
|
||||
public function __construct(AuthenticationException $exception, AuthenticatorInterface $authenticator, Request $request, ?Response $response, string $providerKey)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
$this->authenticator = $authenticator;
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->providerKey = $providerKey;
|
||||
}
|
||||
|
||||
public function getException(): AuthenticationException
|
||||
{
|
||||
return $this->exception;
|
||||
}
|
||||
|
||||
public function getAuthenticator(): AuthenticatorInterface
|
||||
{
|
||||
return $this->authenticator;
|
||||
}
|
||||
|
||||
public function getProviderKey(): string
|
||||
{
|
||||
return $this->providerKey;
|
||||
}
|
||||
|
||||
public function getRequest(): Request
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getResponse(): ?Response
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\Event;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* This event is dispatched after authentication has successfully completed.
|
||||
*
|
||||
* At this stage, the authenticator created an authenticated token
|
||||
* and generated an authentication success response. Listeners to
|
||||
* this event can do actions related to successful authentication
|
||||
* (such as migrating the password).
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class LoginSuccessEvent extends Event
|
||||
{
|
||||
private $authenticator;
|
||||
private $authenticatedToken;
|
||||
private $request;
|
||||
private $response;
|
||||
private $providerKey;
|
||||
|
||||
public function __construct(AuthenticatorInterface $authenticator, TokenInterface $authenticatedToken, Request $request, ?Response $response, string $providerKey)
|
||||
{
|
||||
$this->authenticator = $authenticator;
|
||||
$this->authenticatedToken = $authenticatedToken;
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->providerKey = $providerKey;
|
||||
}
|
||||
|
||||
public function getAuthenticator(): AuthenticatorInterface
|
||||
{
|
||||
return $this->authenticator;
|
||||
}
|
||||
|
||||
public function getAuthenticatedToken(): TokenInterface
|
||||
{
|
||||
return $this->authenticatedToken;
|
||||
}
|
||||
|
||||
public function getRequest(): Request
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getResponse(): ?Response
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function getProviderKey(): string
|
||||
{
|
||||
return $this->providerKey;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\Event;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* This event is dispatched when the credentials have to be checked.
|
||||
*
|
||||
* Listeners to this event must validate the user and the
|
||||
* credentials (e.g. default listeners do password verification and
|
||||
* user checking)
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
class VerifyAuthenticatorCredentialsEvent extends Event
|
||||
{
|
||||
private $authenticator;
|
||||
private $preAuthenticatedToken;
|
||||
private $user;
|
||||
private $credentialsValid = false;
|
||||
|
||||
public function __construct(AuthenticatorInterface $authenticator, TokenInterface $preAuthenticatedToken, ?UserInterface $user)
|
||||
{
|
||||
$this->authenticator = $authenticator;
|
||||
$this->preAuthenticatedToken = $preAuthenticatedToken;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getAuthenticator(): AuthenticatorInterface
|
||||
{
|
||||
return $this->authenticator;
|
||||
}
|
||||
|
||||
public function getPreAuthenticatedToken(): TokenInterface
|
||||
{
|
||||
return $this->preAuthenticatedToken;
|
||||
}
|
||||
|
||||
public function getUser(): ?UserInterface
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setCredentialsValid(bool $validated = true): void
|
||||
{
|
||||
$this->credentialsValid = $validated;
|
||||
}
|
||||
|
||||
public function areCredentialsValid(): bool
|
||||
{
|
||||
return $this->credentialsValid;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\Core\Exception\LogicException;
|
||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\CustomAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\TokenAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||
|
||||
/**
|
||||
* This listeners uses the interfaces of authenticators to
|
||||
* determine how to check credentials.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@driveamber.com>
|
||||
*
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class AuthenticatingListener implements EventSubscriberInterface
|
||||
{
|
||||
private $encoderFactory;
|
||||
|
||||
public function __construct(EncoderFactoryInterface $encoderFactory)
|
||||
{
|
||||
$this->encoderFactory = $encoderFactory;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [VerifyAuthenticatorCredentialsEvent::class => ['onAuthenticating', 128]];
|
||||
}
|
||||
|
||||
public function onAuthenticating(VerifyAuthenticatorCredentialsEvent $event): void
|
||||
{
|
||||
$authenticator = $event->getAuthenticator();
|
||||
if ($authenticator instanceof PasswordAuthenticatedInterface) {
|
||||
// Use the password encoder to validate the credentials
|
||||
$user = $event->getUser();
|
||||
$event->setCredentialsValid($this->encoderFactory->getEncoder($user)->isPasswordValid(
|
||||
$user->getPassword(),
|
||||
$authenticator->getPassword($event->getPreAuthenticatedToken()->getCredentials()),
|
||||
$user->getSalt()
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($authenticator instanceof TokenAuthenticatedInterface) {
|
||||
if (null !== $authenticator->getToken($event->getCredentials())) {
|
||||
// Token based authenticators do not have a credential validation step
|
||||
$event->setCredentialsValid();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($authenticator instanceof CustomAuthenticatedInterface) {
|
||||
$event->setCredentialsValid($authenticator->checkCredentials($event->getPreAuthenticatedToken()->getCredentials(), $event->getUser()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new LogicException(sprintf('Authenticator %s does not have valid credentials. Authenticators must implement one of the authenticated interfaces (%s, %s or %s).', \get_class($authenticator), PasswordAuthenticatedInterface::class, TokenAuthenticatedInterface::class, CustomAuthenticatedInterface::class));
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
|
||||
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class PasswordMigratingListener implements EventSubscriberInterface
|
||||
{
|
||||
private $encoderFactory;
|
||||
|
||||
public function __construct(EncoderFactoryInterface $encoderFactory)
|
||||
{
|
||||
$this->encoderFactory = $encoderFactory;
|
||||
}
|
||||
|
||||
public function onCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
||||
{
|
||||
if (!$event->areCredentialsValid()) {
|
||||
// Do not migrate password that are not validated
|
||||
return;
|
||||
}
|
||||
|
||||
$authenticator = $event->getAuthenticator();
|
||||
if (!$authenticator instanceof PasswordAuthenticatedInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$token = $event->getPreAuthenticatedToken();
|
||||
if (null !== $password = $authenticator->getPassword($token->getCredentials())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $token->getUser();
|
||||
if (!$user instanceof UserInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$passwordEncoder = $this->encoderFactory->getEncoder($user);
|
||||
if (!method_exists($passwordEncoder, 'needsRehash') || !$passwordEncoder->needsRehash($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$authenticator instanceof PasswordUpgraderInterface) {
|
||||
return;
|
||||
}
|
||||
|
||||
$authenticator->upgradePassword($user, $passwordEncoder->encodePassword($user, $password));
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [VerifyAuthenticatorCredentialsEvent::class => ['onCredentialsVerification', -128]];
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\EventListener;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class RememberMeListener implements EventSubscriberInterface
|
||||
{
|
||||
private $providerKey;
|
||||
private $logger;
|
||||
/** @var RememberMeServicesInterface|null */
|
||||
private $rememberMeServices;
|
||||
|
||||
public function __construct(string $providerKey, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->providerKey = $providerKey;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
||||
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices): void
|
||||
{
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
}
|
||||
|
||||
public function onSuccessfulLogin(LoginSuccessEvent $event): void
|
||||
{
|
||||
if (!$this->isRememberMeEnabled($event->getAuthenticator(), $event->getProviderKey())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->rememberMeServices->loginSuccess($event->getRequest(), $event->getResponse(), $event->getAuthenticatedToken());
|
||||
}
|
||||
|
||||
public function onFailedLogin(LoginFailureEvent $event): void
|
||||
{
|
||||
if (!$this->isRememberMeEnabled($event->getAuthenticator(), $event->getProviderKey())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->rememberMeServices->loginFail($event->getRequest(), $event->getException());
|
||||
}
|
||||
|
||||
private function isRememberMeEnabled(AuthenticatorInterface $authenticator, string $providerKey): bool
|
||||
{
|
||||
if ($providerKey !== $this->providerKey) {
|
||||
// This listener is created for a different firewall.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $this->rememberMeServices) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$authenticator->supportsRememberMe()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
LoginSuccessEvent::class => 'onSuccessfulLogin',
|
||||
LoginFailureEvent::class => 'onFailedLogin',
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Security\Http\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @final
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class UserCheckerListener implements EventSubscriberInterface
|
||||
{
|
||||
private $userChecker;
|
||||
|
||||
public function __construct(UserCheckerInterface $userChecker)
|
||||
{
|
||||
$this->userChecker = $userChecker;
|
||||
}
|
||||
|
||||
public function preCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
||||
{
|
||||
$this->userChecker->checkPreAuth($event->getUser());
|
||||
}
|
||||
|
||||
public function postCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
||||
{
|
||||
$this->userChecker->checkPostAuth($event->getUser());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
VerifyAuthenticatorCredentialsEvent::class => [
|
||||
['preCredentialsVerification', 256],
|
||||
['preCredentialsVerification', 32]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -12,12 +12,18 @@
|
||||
namespace Symfony\Component\Security\Http\Firewall;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken;
|
||||
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
@ -34,19 +40,20 @@ class GuardManagerListener
|
||||
private $guardHandler;
|
||||
private $guardAuthenticators;
|
||||
protected $providerKey;
|
||||
private $eventDispatcher;
|
||||
protected $logger;
|
||||
private $rememberMeServices;
|
||||
|
||||
/**
|
||||
* @param AuthenticatorInterface[] $guardAuthenticators
|
||||
*/
|
||||
public function __construct(AuthenticationManagerInterface $authenticationManager, GuardAuthenticatorHandler $guardHandler, iterable $guardAuthenticators, string $providerKey, ?LoggerInterface $logger = null)
|
||||
public function __construct(AuthenticationManagerInterface $authenticationManager, GuardAuthenticatorHandler $guardHandler, iterable $guardAuthenticators, string $providerKey, EventDispatcherInterface $eventDispatcher, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->authenticationManager = $authenticationManager;
|
||||
$this->guardHandler = $guardHandler;
|
||||
$this->guardAuthenticators = $guardAuthenticators;
|
||||
$this->providerKey = $providerKey;
|
||||
$this->logger = $logger;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function __invoke(RequestEvent $requestEvent)
|
||||
@ -57,23 +64,95 @@ class GuardManagerListener
|
||||
return;
|
||||
}
|
||||
|
||||
$this->executeGuardAuthenticators($guardAuthenticators, $requestEvent);
|
||||
$this->executeAuthenticators($guardAuthenticators, $requestEvent);
|
||||
}
|
||||
|
||||
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
|
||||
/**
|
||||
* @param AuthenticatorInterface[] $authenticators
|
||||
*/
|
||||
protected function executeAuthenticators(array $authenticators, RequestEvent $event): void
|
||||
{
|
||||
$this->rememberMeServices = $rememberMeServices;
|
||||
foreach ($authenticators as $key => $guardAuthenticator) {
|
||||
$this->executeAuthenticator($key, $guardAuthenticator, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken
|
||||
{
|
||||
return new PreAuthenticationGuardToken($credentials, $uniqueGuardKey, $providerKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getGuardKey(string $key): string
|
||||
private function executeAuthenticator(string $uniqueAuthenticatorKey, AuthenticatorInterface $authenticator, RequestEvent $event): void
|
||||
{
|
||||
// Guard authenticators in the GuardManagerListener are already indexed
|
||||
// by an unique key
|
||||
return $key;
|
||||
$request = $event->getRequest();
|
||||
try {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Calling getCredentials() on authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
// allow the authenticator to fetch authentication info from the request
|
||||
$credentials = $authenticator->getCredentials($request);
|
||||
|
||||
if (null === $credentials) {
|
||||
throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', \get_class($authenticator)));
|
||||
}
|
||||
|
||||
// create a token with the unique key, so that the provider knows which authenticator to use
|
||||
$token = $this->createPreAuthenticatedToken($credentials, $uniqueAuthenticatorKey, $this->providerKey);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Passing token information to the AuthenticatorManager', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
// pass the token into the AuthenticationManager system
|
||||
// this indirectly calls AuthenticatorManager::authenticate()
|
||||
$token = $this->authenticationManager->authenticate($token);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Authenticator successful!', ['token' => $token, 'authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
// sets the token on the token storage, etc
|
||||
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
|
||||
} catch (AuthenticationException $e) {
|
||||
// oh no! Authentication failed!
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Authenticator failed.', ['exception' => $e, 'authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $authenticator, $this->providerKey);
|
||||
|
||||
if ($response instanceof Response) {
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(new LoginFailureEvent($e, $authenticator, $request, $response, $this->providerKey));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// success!
|
||||
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $authenticator, $this->providerKey);
|
||||
if ($response instanceof Response) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Authenticator set success response.', ['response' => $response, 'authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Authenticator set no success response: request continues.', ['authenticator' => \get_class($authenticator)]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(new LoginSuccessEvent($authenticator, $token, $request, $response, $this->providerKey));
|
||||
}
|
||||
|
||||
protected function createPreAuthenticatedToken($credentials, string $uniqueAuthenticatorKey, string $providerKey): PreAuthenticationGuardToken
|
||||
{
|
||||
return new PreAuthenticationGuardToken($credentials, $uniqueAuthenticatorKey, $providerKey);
|
||||
}
|
||||
}
|
||||
|
@ -46,134 +46,5 @@ trait GuardManagerListenerTrait
|
||||
return $guardAuthenticators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param (CoreAuthenticatorInterface|AuthenticatorInterface)[] $guardAuthenticators
|
||||
*/
|
||||
protected function executeGuardAuthenticators(array $guardAuthenticators, RequestEvent $event): void
|
||||
{
|
||||
foreach ($guardAuthenticators as $key => $guardAuthenticator) {
|
||||
$uniqueGuardKey = $this->getGuardKey($key);
|
||||
|
||||
$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator
|
||||
*/
|
||||
private function executeGuardAuthenticator(string $uniqueGuardKey, $guardAuthenticator, RequestEvent $event)
|
||||
{
|
||||
if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof CoreAuthenticatorInterface) {
|
||||
throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.');
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
try {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
// allow the authenticator to fetch authentication info from the request
|
||||
$credentials = $guardAuthenticator->getCredentials($request);
|
||||
|
||||
if (null === $credentials) {
|
||||
throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
// create a token with the unique key, so that the provider knows which authenticator to use
|
||||
$token = $this->createPreAuthenticatedToken($credentials, $uniqueGuardKey, $this->providerKey);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
// pass the token into the AuthenticationManager system
|
||||
// this indirectly calls GuardAuthenticationProvider::authenticate()
|
||||
$token = $this->authenticationManager->authenticate($token);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Guard authentication successful!', ['token' => $token, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
// sets the token on the token storage, etc
|
||||
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
|
||||
} catch (AuthenticationException $e) {
|
||||
// oh no! Authentication failed!
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('Guard authentication failed.', ['exception' => $e, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
|
||||
|
||||
if ($response instanceof Response) {
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// success!
|
||||
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
|
||||
if ($response instanceof Response) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Guard authenticator set success response.', ['response' => $response, 'authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
} else {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to trigger the remember me functionality
|
||||
$this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if remember me is supported in the authenticator and
|
||||
* on the firewall. If it is, the RememberMeServicesInterface is notified.
|
||||
*
|
||||
* @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator
|
||||
*/
|
||||
private function triggerRememberMe($guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
|
||||
{
|
||||
if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof CoreAuthenticatorInterface) {
|
||||
throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.');
|
||||
}
|
||||
|
||||
if (null === $this->rememberMeServices) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$guardAuthenticator->supportsRememberMe()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
throw new \LogicException(sprintf('"%s::onAuthenticationSuccess()" *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.', get_debug_type($guardAuthenticator)));
|
||||
}
|
||||
|
||||
$this->rememberMeServices->loginSuccess($request, $response, $token);
|
||||
}
|
||||
|
||||
abstract protected function getGuardKey(string $key): string;
|
||||
|
||||
abstract protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken;
|
||||
}
|
||||
|
Reference in New Issue
Block a user