diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php
new file mode 100644
index 0000000000..e721ee77fe
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php
@@ -0,0 +1,113 @@
+
+ */
+class GuardAuthenticationFactory implements SecurityFactoryInterface
+{
+ public function getPosition()
+ {
+ return 'pre_auth';
+ }
+
+ public function getKey()
+ {
+ return 'guard';
+ }
+
+ public function addConfiguration(NodeDefinition $node)
+ {
+ $node
+ ->fixXmlConfig('authenticator')
+ ->children()
+ ->scalarNode('provider')
+ ->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall')
+ ->end()
+ ->scalarNode('entry_point')
+ ->info('A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication')
+ ->defaultValue(null)
+ ->end()
+ ->arrayNode('authenticators')
+ ->info('An array of service ids for all of your "authenticators"')
+ ->requiresAtLeastOneElement()
+ ->prototype('scalar')->end()
+ ->end()
+ ->end()
+ ;
+ }
+
+ public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
+ {
+ $authenticatorIds = $config['authenticators'];
+ $authenticatorReferences = array();
+ foreach ($authenticatorIds as $authenticatorId) {
+ $authenticatorReferences[] = new Reference($authenticatorId);
+ }
+
+ // configure the GuardAuthenticationFactory to have the dynamic constructor arguments
+ $providerId = 'security.authentication.provider.guard.'.$id;
+ $container
+ ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.guard'))
+ ->replaceArgument(0, $authenticatorReferences)
+ ->replaceArgument(1, new Reference($userProvider))
+ ->replaceArgument(2, $id)
+ ;
+
+ // listener
+ $listenerId = 'security.authentication.listener.guard.'.$id;
+ $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard'));
+ $listener->replaceArgument(2, $id);
+ $listener->replaceArgument(3, $authenticatorReferences);
+
+ // determine the entryPointId to use
+ $entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);
+
+ // this is always injected - then the listener decides if it should be used
+ $container
+ ->getDefinition($listenerId)
+ ->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider));
+
+ return array($providerId, $listenerId, $entryPointId);
+ }
+
+ private function determineEntryPoint($defaultEntryPointId, array $config)
+ {
+ if ($defaultEntryPointId) {
+ // explode if they've configured the entry_point, but there is already one
+ if ($config['entry_point']) {
+ throw new \LogicException(sprintf(
+ 'The guard authentication provider cannot use the "%s" entry_point because another entry point is already configured by another provider! Either remove the other provider or move the entry_point configuration as a root key under your firewall',
+ $config['entry_point']
+ ));
+ }
+
+ return $defaultEntryPointId;
+ }
+
+ if ($config['entry_point']) {
+ // if it's configured explicitly, use it!
+ return $config['entry_point'];
+ }
+
+ $authenticatorIds = $config['authenticators'];
+ if (count($authenticatorIds) == 1) {
+ // if there is only one authenticator, use that as the entry point
+ return array_shift($authenticatorIds);
+ }
+
+ // we have multiple entry points - we must ask them to configure one
+ throw new \LogicException(sprintf(
+ 'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of you configurators (%s)',
+ implode(', ', $authenticatorIds)
+ ));
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index e82e3ca877..6db34e4d28 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -65,6 +65,7 @@ class SecurityExtension extends Extension
$loader->load('templating_php.xml');
$loader->load('templating_twig.xml');
$loader->load('collectors.xml');
+ $loader->load('guard.xml');
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
$container->removeDefinition('security.expression_language');
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml
new file mode 100644
index 0000000000..0524cf2b95
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php
new file mode 100644
index 0000000000..1f107879db
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php
@@ -0,0 +1,181 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory;
+
+use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+class GuardAuthenticationFactoryTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getValidConfigurationTests
+ */
+ public function testAddValidConfiguration(array $inputConfig, array $expectedConfig)
+ {
+ $factory = new GuardAuthenticationFactory();
+ $nodeDefinition = new ArrayNodeDefinition('guard');
+ $factory->addConfiguration($nodeDefinition);
+
+ $node = $nodeDefinition->getNode();
+ $normalizedConfig = $node->normalize($inputConfig);
+ $finalizedConfig = $node->finalize($normalizedConfig);
+
+ $this->assertEquals($expectedConfig, $finalizedConfig);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
+ * @dataProvider getInvalidConfigurationTests
+ */
+ public function testAddInvalidConfiguration(array $inputConfig)
+ {
+ $factory = new GuardAuthenticationFactory();
+ $nodeDefinition = new ArrayNodeDefinition('guard');
+ $factory->addConfiguration($nodeDefinition);
+
+ $node = $nodeDefinition->getNode();
+ $normalizedConfig = $node->normalize($inputConfig);
+ // will validate and throw an exception on invalid
+ $node->finalize($normalizedConfig);
+ }
+
+ public function getValidConfigurationTests()
+ {
+ $tests = array();
+
+ // completely basic
+ $tests[] = array(
+ array(
+ 'authenticators' => array('authenticator1', 'authenticator2'),
+ 'provider' => 'some_provider',
+ 'entry_point' => 'the_entry_point'
+ ),
+ array(
+ 'authenticators' => array('authenticator1', 'authenticator2'),
+ 'provider' => 'some_provider',
+ 'entry_point' => 'the_entry_point'
+ )
+ );
+
+ // testing xml config fix: authenticator -> authenticators
+ $tests[] = array(
+ array(
+ 'authenticator' => array('authenticator1', 'authenticator2'),
+ ),
+ array(
+ 'authenticators' => array('authenticator1', 'authenticator2'),
+ 'entry_point' => null,
+ )
+ );
+
+ return $tests;
+ }
+
+ public function getInvalidConfigurationTests()
+ {
+ $tests = array();
+
+ // testing not empty
+ $tests[] = array(
+ array('authenticators' => array())
+ );
+
+ return $tests;
+ }
+
+ public function testBasicCreate()
+ {
+ // simple configuration
+ $config = array(
+ 'authenticators' => array('authenticator123'),
+ 'entry_point' => null,
+ );
+ list($container, $entryPointId) = $this->executeCreate($config, null);
+ $this->assertEquals('authenticator123', $entryPointId);
+
+ $providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall');
+ $this->assertEquals(array(
+ 'index_0' => array(new Reference('authenticator123')),
+ 'index_1' => new Reference('my_user_provider'),
+ 'index_2' => 'my_firewall'
+ ), $providerDefinition->getArguments());
+
+ $listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall');
+ $this->assertEquals('my_firewall', $listenerDefinition->getArgument(2));
+ $this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3));
+ }
+
+ public function testExistingDefaultEntryPointUsed()
+ {
+ // any existing default entry point is used
+ $config = array(
+ 'authenticators' => array('authenticator123'),
+ 'entry_point' => null,
+ );
+ list($container, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point');
+ $this->assertEquals('some_default_entry_point', $entryPointId);
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testCannotOverrideDefaultEntryPoint()
+ {
+ // any existing default entry point is used
+ $config = array(
+ 'authenticators' => array('authenticator123'),
+ 'entry_point' => 'authenticator123',
+ );
+ $this->executeCreate($config, 'some_default_entry_point');
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testMultipleAuthenticatorsRequiresEntryPoint()
+ {
+ // any existing default entry point is used
+ $config = array(
+ 'authenticators' => array('authenticator123', 'authenticatorABC'),
+ 'entry_point' => null,
+ );
+ $this->executeCreate($config, null);
+ }
+
+
+ public function testCreateWithEntryPoint()
+ {
+ // any existing default entry point is used
+ $config = array(
+ 'authenticators' => array('authenticator123', 'authenticatorABC'),
+ 'entry_point' => 'authenticatorABC',
+ );
+ list($container, $entryPointId) = $this->executeCreate($config, null);
+ $this->assertEquals('authenticatorABC', $entryPointId);
+ }
+
+ private function executeCreate(array $config, $defaultEntryPointId)
+ {
+ $container = new ContainerBuilder();
+ $container->register('security.authentication.provider.guard');
+ $container->register('security.authentication.listener.guard');
+ $id = 'my_firewall';
+ $userProviderId = 'my_user_provider';
+
+ $factory = new GuardAuthenticationFactory();
+ list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
+
+ return array($container, $entryPointId);
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php
new file mode 100644
index 0000000000..ebd09bb73f
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php
@@ -0,0 +1,31 @@
+
+ */
+abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
+{
+ /**
+ * Shortcut to create a GenericGuardToken for you, if you don't really
+ * care about which authenticated token you're using
+ *
+ * @param UserInterface $user
+ * @param string $providerKey
+ * @return GenericGuardToken
+ */
+ public function createAuthenticatedToken(UserInterface $user, $providerKey)
+ {
+ return new GenericGuardToken(
+ $user,
+ $providerKey,
+ $user->getRoles()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php
new file mode 100644
index 0000000000..a53478a2b7
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php
@@ -0,0 +1,180 @@
+
+ */
+class GuardAuthenticationListener implements ListenerInterface
+{
+ private $guardHandler;
+ private $authenticationManager;
+ private $providerKey;
+ private $guardAuthenticators;
+ private $logger;
+ private $rememberMeServices;
+
+ /**
+ * @param GuardAuthenticatorHandler $guardHandler The Guard handler
+ * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
+ * @param LoggerInterface $logger A LoggerInterface instance
+ */
+ public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null)
+ {
+ if (empty($providerKey)) {
+ throw new \InvalidArgumentException('$providerKey must not be empty.');
+ }
+
+ $this->guardHandler = $guardHandler;
+ $this->authenticationManager = $authenticationManager;
+ $this->providerKey = $providerKey;
+ $this->guardAuthenticators = $guardAuthenticators;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Iterates over each authenticator to see if each wants to authenticate the request
+ *
+ * @param GetResponseEvent $event
+ */
+ public function handle(GetResponseEvent $event)
+ {
+ if (null !== $this->logger) {
+ $this->logger->info('Checking for guard authentication credentials', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
+ }
+
+ foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
+ // get a key that's unique to *this* guard authenticator
+ // this MUST be the same as GuardAuthenticationProvider
+ $uniqueGuardKey = $this->providerKey.'_'.$key;
+
+ $this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
+ }
+ }
+
+ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorInterface $guardAuthenticator, GetResponseEvent $event)
+ {
+ $request = $event->getRequest();
+ try {
+ if (null !== $this->logger) {
+ $this->logger->info('Calling getCredentialsFromRequest on guard configurator', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
+ }
+
+ // allow the authenticator to fetch authentication info from the request
+ $credentials = $guardAuthenticator->getCredentialsFromRequest($request);
+
+ // allow null to be returned to skip authentication
+ if (null === $credentials) {
+ return;
+ }
+
+ // create a token with the unique key, so that the provider knows which authenticator to use
+ $token = new NonAuthenticatedGuardToken($credentials, $uniqueGuardKey);
+
+ if (null !== $this->logger) {
+ $this->logger->info('Passing guard token information to the GuardAuthenticationProvider', array('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!', array('token' => $token, 'authenticator' => get_class($guardAuthenticator)));
+ }
+
+ // sets the token on the token storage, etc
+ $this->guardHandler->authenticateWithToken($token, $request);
+ } catch (AuthenticationException $e) {
+ // oh no! Authentication failed!
+
+ if (null !== $this->logger) {
+ $this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator)));
+ }
+
+ $response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator);
+
+ 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->info('Guard authenticator set success response', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
+ }
+
+ $event->setResponse($response);
+ } else {
+ if (null !== $this->logger) {
+ $this->logger->info('Guard authenticator set no success response: request continues', array('authenticator' => get_class($guardAuthenticator)));
+ }
+ }
+
+ // attempt to trigger the remember me functionality
+ $this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
+ }
+
+ /**
+ * Should be called if this listener will support remember me.
+ *
+ * @param RememberMeServicesInterface $rememberMeServices
+ */
+ public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
+ {
+ $this->rememberMeServices = $rememberMeServices;
+ }
+
+ /**
+ * Checks to see if remember me is supported in the authenticator and
+ * on the firewall. If it is, the RememberMeServicesInterface is notified
+ *
+ * @param GuardAuthenticatorInterface $guardAuthenticator
+ * @param Request $request
+ * @param TokenInterface $token
+ * @param Response $response
+ */
+ private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
+ {
+ if (!$guardAuthenticator->supportsRememberMe()) {
+ return;
+ }
+
+ if (null === $this->rememberMeServices) {
+ if (null !== $this->logger) {
+ $this->logger->info('Remember me skipped: it is not configured for the firewall', array('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_class($guardAuthenticator)
+ ));
+ }
+
+ $this->rememberMeServices->loginSuccess($request, $response, $token);
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php
new file mode 100644
index 0000000000..e574613f7a
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php
@@ -0,0 +1,122 @@
+
+ */
+class GuardAuthenticatorHandler
+{
+ private $tokenStorage;
+
+ private $dispatcher;
+
+ public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
+ {
+ $this->tokenStorage = $tokenStorage;
+ $this->dispatcher = $eventDispatcher;
+ }
+
+ /**
+ * Authenticates the given token in the system
+ *
+ * @param TokenInterface $token
+ * @param Request $request
+ */
+ public function authenticateWithToken(TokenInterface $token, Request $request)
+ {
+ $this->tokenStorage->setToken($token);
+
+ if (null !== $this->dispatcher) {
+ $loginEvent = new InteractiveLoginEvent($request, $token);
+ $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
+ }
+ }
+
+ /**
+ * Returns the "on success" response for the given GuardAuthenticator
+ *
+ * @param TokenInterface $token
+ * @param Request $request
+ * @param GuardAuthenticatorInterface $guardAuthenticator
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @return null|Response
+ */
+ public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
+ {
+ $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey);
+
+ // check that it's a Response or null
+ if ($response instanceof Response || null === $response) {
+ return $response;
+ }
+
+ throw new \UnexpectedValueException(sprintf(
+ 'The %s::onAuthenticationSuccess method must return null or a Response object. You returned %s',
+ get_class($guardAuthenticator),
+ is_object($response) ? get_class($response) : gettype($response)
+ ));
+ }
+
+ /**
+ * Convenience method for authenticating the user and returning the
+ * Response *if any* for success
+ *
+ * @param UserInterface $user
+ * @param Request $request
+ * @param GuardAuthenticatorInterface $authenticator
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @return Response|null
+ */
+ public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, GuardAuthenticatorInterface $authenticator, $providerKey)
+ {
+ // create an authenticated token for the User
+ $token = $authenticator->createAuthenticatedToken($user, $providerKey);
+ // authenticate this in the system
+ $this->authenticateWithToken($token, $request);
+
+ // return the success metric
+ return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
+ }
+
+ /**
+ * Handles an authentication failure and returns the Response for the
+ * GuardAuthenticator
+ *
+ * @param AuthenticationException $authenticationException
+ * @param Request $request
+ * @param GuardAuthenticatorInterface $guardAuthenticator
+ * @return null|Response
+ */
+ public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator)
+ {
+ $this->tokenStorage->setToken(null);
+
+ $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
+ if ($response instanceof Response || null === $response) {
+ // returning null is ok, it means they want the request to continue
+ return $response;
+ }
+
+ throw new \UnexpectedValueException(sprintf(
+ 'The %s::onAuthenticationFailure method must return null or a Response object. You returned %s',
+ get_class($guardAuthenticator),
+ is_object($response) ? get_class($response) : gettype($response)
+ ));
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php
new file mode 100644
index 0000000000..dba8d09e22
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php
@@ -0,0 +1,119 @@
+
+ */
+interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
+{
+ /**
+ * Get the authentication credentials from the request and return them
+ * as any type (e.g. an associate array). If you return null, authentication
+ * will be skipped.
+ *
+ * Whatever value you return here will be passed to authenticate()
+ *
+ * For example, for a form login, you might:
+ *
+ * return array(
+ * 'username' => $request->request->get('_username'),
+ * 'password' => $request->request->get('_password'),
+ * );
+ *
+ * Or for an API token that's on a header, you might use:
+ *
+ * return array('api_key' => $request->headers->get('X-API-TOKEN'));
+ *
+ * @param Request $request
+ * @return mixed|null
+ */
+ public function getCredentialsFromRequest(Request $request);
+
+ /**
+ * Return a UserInterface object based on the credentials OR throw
+ * an AuthenticationException
+ *
+ * The *credentials* are the return value from getCredentialsFromRequest()
+ *
+ * @param mixed $credentials
+ * @param UserProviderInterface $userProvider
+ * @throws AuthenticationException
+ * @return UserInterface
+ */
+ public function authenticate($credentials, UserProviderInterface $userProvider);
+
+ /**
+ * Create an authenticated token for the given user
+ *
+ * If you don't care about which token class is used or don't really
+ * understand what a "token" is, you can skip this method by extending
+ * the AbstractGuardAuthenticator class from your authenticator.
+ *
+ * @see AbstractGuardAuthenticator
+ * @param UserInterface $user
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @return TokenInterface
+ */
+ public function createAuthenticatedToken(UserInterface $user, $providerKey);
+
+ /**
+ * Called when authentication executed, but failed (e.g. wrong username password)
+ *
+ * This should return the Response sent back to the user, like a
+ * RedirectResponse to the login page or a 403 response.
+ *
+ * If you return null, the request will continue, but the user will
+ * not be authenticated. This is probably not what you want to do.
+ *
+ * @param Request $request
+ * @param AuthenticationException $exception
+ * @return Response|null
+ */
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception);
+
+ /**
+ * Called when authentication executed and was successful!
+ *
+ * This should return the Response sent back to the user, like a
+ * RedirectResponse to the last page they visited.
+ *
+ * If you return null, the current request will continue, and the user
+ * will be authenticated. This makes sense, for example, with an API.
+ *
+ * @param Request $request
+ * @param TokenInterface $token
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @return Response|null
+ */
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey);
+
+ /**
+ * Does this method support remember me cookies?
+ *
+ * Remember me cookie will be set if *all* of the following are met:
+ * A) This method returns true
+ * B) The remember_me key under your firewall is configured
+ * C) The "remember me" functionality is activated. This is usually
+ * done by having a _remember_me checkbox in your form, but
+ * can be configured by the "always_remember_me" and "remember_me_parameter"
+ * parameters under the "remember_me" firewall key
+ *
+ * @return bool
+ */
+ public function supportsRememberMe();
+}
diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php
new file mode 100644
index 0000000000..740f4c47c2
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php
@@ -0,0 +1,106 @@
+
+ */
+class GuardAuthenticationProvider implements AuthenticationProviderInterface
+{
+ /**
+ * @var GuardAuthenticatorInterface[]
+ */
+ private $guardAuthenticators;
+ private $userProvider;
+ private $providerKey;
+ private $userChecker;
+
+ /**
+ * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
+ * @param UserProviderInterface $userProvider The user provider
+ * @param string $providerKey The provider (i.e. firewall) key
+ * @param UserCheckerInterface $userChecker
+ */
+ public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
+ {
+ $this->guardAuthenticators = $guardAuthenticators;
+ $this->userProvider = $userProvider;
+ $this->providerKey = $providerKey;
+ $this->userChecker = $userChecker;
+ }
+
+ /**
+ * Finds the correct authenticator for the token and calls it
+ *
+ * @param NonAuthenticatedGuardToken $token
+ * @return TokenInterface
+ */
+ public function authenticate(TokenInterface $token)
+ {
+ if (!$token instanceof NonAuthenticatedGuardToken) {
+ throw new \InvalidArgumentException('GuardAuthenticationProvider only supports NonAuthenticatedGuardToken');
+ }
+
+ // find the *one* GuardAuthenticator that this token originated from
+ foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
+ // get a key that's unique to *this* guard authenticator
+ // this MUST be the same as GuardAuthenticationListener
+ $uniqueGuardKey = $this->providerKey.'_'.$key;
+
+ if ($uniqueGuardKey == $token->getGuardProviderKey()) {
+ return $this->authenticateViaGuard($guardAuthenticator, $token);
+ }
+ }
+
+ throw new \LogicException(sprintf(
+ 'The correct GuardAuthenticator could not be found for unique key "%s". The listener and provider should be passed the same list of authenticators!?',
+ $token->getGuardProviderKey()
+ ));
+ }
+
+ private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, NonAuthenticatedGuardToken $token)
+ {
+ // get the user from the GuardAuthenticator
+ $user = $guardAuthenticator->authenticate($token->getCredentials(), $this->userProvider);
+
+ if (!$user instanceof UserInterface) {
+ throw new \UnexpectedValueException(sprintf(
+ 'The %s::authenticate method must return a UserInterface. You returned %s',
+ get_class($guardAuthenticator),
+ is_object($user) ? get_class($user) : gettype($user)
+ ));
+ }
+
+ // check the AdvancedUserInterface methods!
+ $this->userChecker->checkPreAuth($user);;
+ $this->userChecker->checkPostAuth($user);
+
+ // turn the UserInterface into a TokenInterface
+ $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
+ if (!$authenticatedToken instanceof TokenInterface) {
+ throw new \UnexpectedValueException(sprintf(
+ 'The %s::createAuthenticatedToken method must return a TokenInterface. You returned %s',
+ get_class($guardAuthenticator),
+ is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken)
+ ));
+ }
+
+ return $authenticatedToken;
+ }
+
+ public function supports(TokenInterface $token)
+ {
+ return $token instanceof NonAuthenticatedGuardToken;
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php
new file mode 100644
index 0000000000..b8939a93dd
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php
@@ -0,0 +1,222 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Guard\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener;
+use Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+
+/**
+ * @author Ryan Weaver
+ */
+class GuardAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ private $authenticationManager;
+ private $guardAuthenticatorHandler;
+ private $event;
+ private $logger;
+ private $request;
+ private $rememberMeServices;
+
+ public function testHandleSuccess()
+ {
+ $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $providerKey = 'my_firewall';
+
+ $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base');
+ $authenticator
+ ->expects($this->once())
+ ->method('getCredentialsFromRequest')
+ ->with($this->equalTo($this->request))
+ ->will($this->returnValue($credentials));
+
+ // a clone of the token that should be created internally
+ $uniqueGuardKey = 'my_firewall_0';
+ $nonAuthedToken = new NonAuthenticatedGuardToken($credentials, $uniqueGuardKey);
+
+ $this->authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->equalTo($nonAuthedToken))
+ ->will($this->returnValue($authenticateToken));
+
+ $this->guardAuthenticatorHandler
+ ->expects($this->once())
+ ->method('authenticateWithToken')
+ ->with($authenticateToken, $this->request);
+
+ $this->guardAuthenticatorHandler
+ ->expects($this->once())
+ ->method('handleAuthenticationSuccess')
+ ->with($authenticateToken, $this->request, $authenticator, $providerKey);
+
+ $listener = new GuardAuthenticationListener(
+ $this->guardAuthenticatorHandler,
+ $this->authenticationManager,
+ $providerKey,
+ array($authenticator),
+ $this->logger
+ );
+
+ $listener->setRememberMeServices($this->rememberMeServices);
+ // should never be called - our handleAuthenticationSuccess() does not return a Response
+ $this->rememberMeServices
+ ->expects($this->never())
+ ->method('loginSuccess');
+
+ $listener->handle($this->event);
+ }
+
+ public function testHandleSuccessWithRememberMe()
+ {
+ $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $providerKey = 'my_firewall_with_rememberme';
+
+ $authenticator
+ ->expects($this->once())
+ ->method('getCredentialsFromRequest')
+ ->with($this->equalTo($this->request))
+ ->will($this->returnValue(array('username' => 'anything_not_empty')));
+
+ $this->authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->returnValue($authenticateToken));
+
+ $successResponse = new Response('Success!');
+ $this->guardAuthenticatorHandler
+ ->expects($this->once())
+ ->method('handleAuthenticationSuccess')
+ ->will($this->returnValue($successResponse));
+
+ $listener = new GuardAuthenticationListener(
+ $this->guardAuthenticatorHandler,
+ $this->authenticationManager,
+ $providerKey,
+ array($authenticator),
+ $this->logger
+ );
+
+ $listener->setRememberMeServices($this->rememberMeServices);
+ $authenticator->expects($this->once())
+ ->method('supportsRememberMe')
+ ->will($this->returnValue(true));
+ // should be called - we do have a success Response
+ $this->rememberMeServices
+ ->expects($this->once())
+ ->method('loginSuccess');
+
+ $listener->handle($this->event);
+ }
+
+ public function testHandleCatchesAuthenticationException()
+ {
+ $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $providerKey = 'my_firewall2';
+
+ $authException = new AuthenticationException('Get outta here crazy user with a bad password!');
+ $authenticator
+ ->expects($this->once())
+ ->method('getCredentialsFromRequest')
+ ->will($this->throwException($authException));
+
+ // this is not called
+ $this->authenticationManager
+ ->expects($this->never())
+ ->method('authenticate');
+
+ $this->guardAuthenticatorHandler
+ ->expects($this->once())
+ ->method('handleAuthenticationFailure')
+ ->with($authException, $this->request, $authenticator);
+
+ $listener = new GuardAuthenticationListener(
+ $this->guardAuthenticatorHandler,
+ $this->authenticationManager,
+ $providerKey,
+ array($authenticator),
+ $this->logger
+ );
+
+ $listener->handle($this->event);
+ }
+
+ public function testReturnNullToSkipAuth()
+ {
+ $authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $providerKey = 'my_firewall3';
+
+ $authenticatorA
+ ->expects($this->once())
+ ->method('getCredentialsFromRequest')
+ ->will($this->returnValue(null));
+ $authenticatorB
+ ->expects($this->once())
+ ->method('getCredentialsFromRequest')
+ ->will($this->returnValue(null));
+
+ // this is not called
+ $this->authenticationManager
+ ->expects($this->never())
+ ->method('authenticate');
+
+ $this->guardAuthenticatorHandler
+ ->expects($this->never())
+ ->method('handleAuthenticationSuccess');
+
+ $listener = new GuardAuthenticationListener(
+ $this->guardAuthenticatorHandler,
+ $this->authenticationManager,
+ $providerKey,
+ array($authenticatorA, $authenticatorB),
+ $this->logger
+ );
+
+ $listener->handle($this->event);
+ }
+
+ protected function setUp()
+ {
+ $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->guardAuthenticatorHandler = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorHandler')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->request = new Request(array(), array(), array(), array(), array(), array());
+
+ $this->event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $this->event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($this->request));
+
+ $this->logger = $this->getMock('Psr\Log\LoggerInterface');
+ $this->rememberMeServices = $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface');
+ }
+
+ protected function tearDown()
+ {
+ $this->authenticationManager = null;
+ $this->guardAuthenticatorHandler = null;
+ $this->event = null;
+ $this->logger = null;
+ $this->request = null;
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php
new file mode 100644
index 0000000000..6b27e209bb
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php
@@ -0,0 +1,99 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Guard\Tests;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
+use Symfony\Component\Security\Http\SecurityEvents;
+
+/**
+ * @author Ryan Weaver
+ */
+class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ private $tokenStorage;
+ private $dispatcher;
+ private $token;
+ private $request;
+ private $guardAuthenticator;
+
+ public function testAuthenticateWithToken()
+ {
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with($this->token);
+
+ $loginEvent = new InteractiveLoginEvent($this->request, $this->token);
+
+ $this->dispatcher
+ ->expects($this->once())
+ ->method('dispatch')
+ ->with($this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent))
+ ;
+
+ $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
+ $handler->authenticateWithToken($this->token, $this->request);
+ }
+
+ public function testHandleAuthenticationSuccess()
+ {
+ $providerKey = 'my_handleable_firewall';
+ $response = new Response('Guard all the things!');
+ $this->guardAuthenticator->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token, $providerKey)
+ ->will($this->returnValue($response));
+
+ $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
+ $actualResponse = $handler->handleAuthenticationSuccess($this->token, $this->request, $this->guardAuthenticator, $providerKey);
+ $this->assertSame($response, $actualResponse);
+ }
+
+ public function testHandleAuthenticationFailure()
+ {
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with(null);
+ $authException = new AuthenticationException('Bad password!');
+
+ $response = new Response('Try again, but with the right password!');
+ $this->guardAuthenticator->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $authException)
+ ->will($this->returnValue($response));
+
+ $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
+ $actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator);
+ $this->assertSame($response, $actualResponse);
+ }
+
+ protected function setUp()
+ {
+ $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $this->request = new Request(array(), array(), array(), array(), array(), array());
+ $this->guardAuthenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ }
+
+ protected function tearDown()
+ {
+ $this->tokenStorage = null;
+ $this->dispatcher = null;
+ $this->token = null;
+ $this->request = null;
+ $this->guardAuthenticator = null;
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
new file mode 100644
index 0000000000..0ef6818659
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Guard\Tests\Provider;
+
+use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider;
+
+/**
+ * @author Ryan Weaver
+ */
+class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
+{
+ private $userProvider;
+ private $userChecker;
+ private $nonAuthedToken;
+
+ public function testAuthenticate()
+ {
+ $providerKey = 'my_cool_firewall';
+
+ $authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticatorC = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
+ $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC);
+
+ // called 2 times - for authenticator A and B (stops on B because of match)
+ $this->nonAuthedToken->expects($this->exactly(2))
+ ->method('getGuardProviderKey')
+ // it will return the "1" index, which will match authenticatorB
+ ->will($this->returnValue('my_cool_firewall_1'));
+
+ $enteredCredentials = array(
+ 'username' => '_weaverryan_test_user',
+ 'password' => 'guard_auth_ftw',
+ );
+ $this->nonAuthedToken->expects($this->once())
+ ->method('getCredentials')
+ ->will($this->returnValue($enteredCredentials));
+
+ // authenticators A and C are never called
+ $authenticatorA->expects($this->never())
+ ->method('authenticate');
+ $authenticatorC->expects($this->never())
+ ->method('authenticate');
+
+ $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $authenticatorB->expects($this->once())
+ ->method('authenticate')
+ ->with($enteredCredentials, $this->userProvider)
+ ->will($this->returnValue($mockedUser));
+ $authedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $authenticatorB->expects($this->once())
+ ->method('createAuthenticatedToken')
+ ->with($mockedUser, $providerKey)
+ ->will($this->returnValue($authedToken));
+
+ // user checker should be called
+ $this->userChecker->expects($this->once())
+ ->method('checkPreAuth')
+ ->with($mockedUser);
+ $this->userChecker->expects($this->once())
+ ->method('checkPostAuth')
+ ->with($mockedUser);
+
+ $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker);
+ $actualAuthedToken = $provider->authenticate($this->nonAuthedToken);
+ $this->assertSame($authedToken, $actualAuthedToken);
+ }
+
+ protected function setUp()
+ {
+ $this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
+ $this->nonAuthedToken = $this->getMockBuilder('Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown()
+ {
+ $this->userProvider = null;
+ $this->userChecker = null;
+ $this->nonAuthedToken = null;
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/Token/GenericGuardToken.php b/src/Symfony/Component/Security/Guard/Token/GenericGuardToken.php
new file mode 100644
index 0000000000..869d0710c8
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Token/GenericGuardToken.php
@@ -0,0 +1,96 @@
+
+ */
+class GenericGuardToken extends AbstractToken
+{
+ private $providerKey;
+
+ /**
+ * @param UserInterface $user The user!
+ * @param string $providerKey The provider (firewall) key
+ * @param RoleInterface[]|string[] $roles An array of roles
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct(UserInterface $user, $providerKey, array $roles)
+ {
+ parent::__construct($roles);
+
+ if (empty($providerKey)) {
+ throw new \InvalidArgumentException('$providerKey (i.e. firewall key) must not be empty.');
+ }
+
+ $this->setUser($user);
+ $this->providerKey = $providerKey;
+
+ // this token is meant to be used after authentication success, so it is always authenticated
+ // you could set it as non authenticated later if you need to
+ parent::setAuthenticated(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAuthenticated($isAuthenticated)
+ {
+ if ($isAuthenticated) {
+ throw new \LogicException('Cannot set this token to trusted after instantiation.');
+ }
+
+ parent::setAuthenticated(false);
+ }
+
+ /**
+ * This is meant to be only an authenticated token, where credentials
+ * have already been used and are thus cleared.
+ *
+ * {@inheritdoc}
+ */
+ public function getCredentials()
+ {
+ return array();
+ }
+
+ /**
+ * Returns the provider (firewall) key.
+ *
+ * @return string
+ */
+ public function getProviderKey()
+ {
+ return $this->providerKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function serialize()
+ {
+ return serialize(array($this->providerKey, parent::serialize()));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unserialize($serialized)
+ {
+ list($this->providerKey, $parentStr) = unserialize($serialized);
+ parent::unserialize($parentStr);
+ }
+}
diff --git a/src/Symfony/Component/Security/Guard/Token/NonAuthenticatedGuardToken.php b/src/Symfony/Component/Security/Guard/Token/NonAuthenticatedGuardToken.php
new file mode 100644
index 0000000000..28e21e0dee
--- /dev/null
+++ b/src/Symfony/Component/Security/Guard/Token/NonAuthenticatedGuardToken.php
@@ -0,0 +1,56 @@
+
+ */
+class NonAuthenticatedGuardToken extends AbstractToken
+{
+ private $credentials;
+ private $guardProviderKey;
+
+ /**
+ * @param mixed $credentials
+ * @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface
+ */
+ public function __construct($credentials, $guardProviderKey)
+ {
+ $this->credentials = $credentials;
+ $this->guardProviderKey = $guardProviderKey;
+
+ parent::__construct(array());
+
+ // never authenticated
+ parent::setAuthenticated(false);
+ }
+
+ public function getGuardProviderKey()
+ {
+ return $this->guardProviderKey;
+ }
+
+ /**
+ * Returns the user credentials, which might be an array of anything you
+ * wanted to put in there (e.g. username, password, favoriteColor).
+ *
+ * @return mixed The user credentials
+ */
+ public function getCredentials()
+ {
+ return $this->credentials;
+ }
+
+ public function setAuthenticated($authenticated)
+ {
+ throw new \Exception('The NonAuthenticatedGuardToken is *always* not authenticated');
+ }
+}