title: symfony2 login using 3 parameters (username, password and company)
body: <p>I have been giving myself a headache over this all day,</p>
  
  <p>I've been learning Symfony over the past couple days and my problem is simple, I need to be able to log in with 3 parameters (username, password and company)</p>
  
  <p>The same question has been asked here, however the asker never provided a solution:
  <a href="https://stackoverflow.com/questions/9652290/custom-login-using-a-third-parameter">Custom Login using a Third Parameter</a></p>
  
  <p>I have tried to start by cloning Symfony's inbuilt login by implementing a Token, Listener, Provider and Factory but whenever I insert my key into security.yml and try to log in it takes me to /login (No route found for "GET /login")</p>
  
  <p>Anyone care to make my day?</p>
  
  <p>EDIT:</p>
  
  <p>OK, I Just tried again and now I have this error:
   Catchable Fatal Error: "Argument 1 passed to Test\SampleBundle\Security\Authentication\Provider\MyProvider::__construct() must be an instance of Symfony\Component\Security\Core\User\UserCheckerInterface, instance of Symfony\Bridge\Doctrine\Security\User\EntityUserProvider given"</p>
  
  <p>Again all I'm trying to do is duplicate Symfonys current user/password login functionality</p>
  
  <p>MyToken.php:
  
  
  <pre><code>namespace Test\SampleBundle\Security\Authentication\Token;
  
  use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
  
  class MyToken extends AbstractToken
  {
  private $credentials;
  private $providerKey;
  
  
  public function __construct($user, $credentials, $providerKey, array $roles = array())
  {
      parent::__construct($roles);
  
      if (empty($providerKey)) {
          throw new \InvalidArgumentException('$providerKey must not be empty.');
      }
  
      $this-&gt;setUser($user);
      $this-&gt;credentials = $credentials;
      $this-&gt;providerKey = $providerKey;
  
      parent::setAuthenticated(count($roles) &gt; 0);
  }
  
  
  public function setAuthenticated($isAuthenticated)
  {
      if ($isAuthenticated) {
          throw new \LogicException('Cannot set this token to trusted after instantiation.');
      }
  
      parent::setAuthenticated(false);
  }
  
  public function getCredentials()
  {
      return $this-&gt;credentials;
  }
  
  public function getProviderKey()
  {
      return $this-&gt;providerKey;
  }
  
  
  public function eraseCredentials()
  {
      parent::eraseCredentials();
  
      $this-&gt;credentials = null;
  }
  
  
  public function serialize()
  {
      return serialize(array($this-&gt;credentials, $this-&gt;providerKey, parent::serialize()));
  }
  
  
  public function unserialize($serialized)
  {
      list($this-&gt;credentials, $this-&gt;providerKey, $parentStr) = unserialize($serialized);
      parent::unserialize($parentStr);
  }
  }
  </code></pre>
  
  <p>MyListener.php:</p>
  
  <pre><code>&lt;?php
  
  
  namespace Test\SampleBundle\Security\Firewall;
  
  use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
  use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
  use Symfony\Component\HttpFoundation\Request;
  use Psr\Log\LoggerInterface;
  use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
  use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
  use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
  use Symfony\Component\Security\Http\HttpUtils;
  use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
  use Test\SampleBundle\Security\Authentication\Token\MyToken;
  use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
  use Symfony\Component\Security\Core\SecurityContextInterface;
  use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  
  
  class MyListener extends AbstractAuthenticationListener
  {
  private $csrfProvider;
  
  
  public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
  {
      parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
          'username_parameter' =&gt; '_username' . 'b',
          'password_parameter' =&gt; '_password',
          'csrf_parameter'     =&gt; '_csrf_token',
          'intention'          =&gt; 'authenticate',
          'post_only'          =&gt; true,
      ), $options), $logger, $dispatcher);
  
      $this-&gt;csrfProvider = $csrfProvider;
  }
  
  
  protected function requiresAuthentication(Request $request)
  {
      if ($this-&gt;options['post_only'] &amp;&amp; !$request-&gt;isMethod('POST')) {
          return false;
      }
  
      return parent::requiresAuthentication($request);
  }
  
  
  protected function attemptAuthentication(Request $request)
  {
      if (null !== $this-&gt;csrfProvider) {
          $csrfToken = $request-&gt;get($this-&gt;options['csrf_parameter'], null, true);
  
          if (false === $this-&gt;csrfProvider-&gt;isCsrfTokenValid($this-&gt;options['intention'], $csrfToken)) {
              throw new InvalidCsrfTokenException('Invalid CSRF token.');
          }
      }
  
      if ($this-&gt;options['post_only']) {
          $username = trim($request-&gt;request-&gt;get($this-&gt;options['username_parameter'], null, true));
          $password = $request-&gt;request-&gt;get($this-&gt;options['password_parameter'], null, true);
      } else {
          $username = trim($request-&gt;get($this-&gt;options['username_parameter'], null, true));
          $password = $request-&gt;get($this-&gt;options['password_parameter'], null, true);
      }
  
      $request-&gt;getSession()-&gt;set(SecurityContextInterface::LAST_USERNAME, $username);
  
      return $this-&gt;authenticationManager-&gt;authenticate(new MyToken($username, $password, $this-&gt;providerKey));
  }
  }
  </code></pre>
  
  <p>MyProivder.php:
      
  
  <pre><code>namespace Test\SampleBundle\Security\Authentication\Provider;
  
  
  use Symfony\Component\Security\Core\User\UserInterface;
  use Symfony\Component\Security\Core\User\UserCheckerInterface;
  use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  use Symfony\Component\Security\Core\Exception\AuthenticationException;
  use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
  use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  use Test\SampleBundle\Security\Authentication\Token\MyToken;
  use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
  
  
  class MyProvider implements AuthenticationProviderInterface
  {
  private $hideUserNotFoundExceptions;
  private $userChecker;
  private $providerKey;
  
  
      public function __construct(UserCheckerInterface $userChecker, $providerKey, $hideUserNotFoundExceptions = true)
  {
      if (empty($providerKey)) {
          throw new \InvalidArgumentException('$providerKey must not be empty.');
      }
  
      $this-&gt;userChecker = $userChecker;
      $this-&gt;providerKey = $providerKey;
      $this-&gt;hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
  }
  
   public function authenticate(TokenInterface $token)
  {
      if (!$this-&gt;supports($token)) {
          return null;
      }
  
      $username = $token-&gt;getUsername();
      if (empty($username)) {
          $username = 'NONE_PROVIDED';
      }
  
      try {
          $user = $this-&gt;retrieveUser($username, $token);
      } catch (UsernameNotFoundException $notFound) {
          if ($this-&gt;hideUserNotFoundExceptions) {
              throw new BadCredentialsException('Bad credentials', 0, $notFound);
          }
          $notFound-&gt;setUsername($username);
  
          throw $notFound;
      }
  
      if (!$user instanceof UserInterface) {
          throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
      }
  
      try {
          $this-&gt;userChecker-&gt;checkPreAuth($user);
          $this-&gt;checkAuthentication($user, $token);
          $this-&gt;userChecker-&gt;checkPostAuth($user);
      } catch (BadCredentialsException $e) {
          if ($this-&gt;hideUserNotFoundExceptions) {
              throw new BadCredentialsException('Bad credentials', 0, $e);
          }
  
          throw $e;
      }
  
      $authenticatedToken = new MyToken($user, $token-&gt;getCredentials(), $this-&gt;providerKey, $user-&gt;getRoles());
      $authenticatedToken-&gt;setAttributes($token-&gt;getAttributes());
  
      return $authenticatedToken;
  }
  
  
      public function supports(TokenInterface $token)
  {
      return $token instanceof MyToken &amp;&amp; $this-&gt;providerKey === $token-&gt;getProviderKey();
  }
  
  /**
   * {@inheritdoc}
   */
  protected function checkAuthentication(UserInterface $user, MyToken $token)
  {
      $currentUser = $token-&gt;getUser();
      if ($currentUser instanceof UserInterface) {
          if ($currentUser-&gt;getPassword() !== $user-&gt;getPassword()) {
              throw new BadCredentialsException('The credentials were changed from another session.');
          }
      } else {
          if ("" === ($presentedPassword = $token-&gt;getCredentials())) {
              throw new BadCredentialsException('The presented password cannot be empty.');
          }
  
          if (!$this-&gt;encoderFactory-&gt;getEncoder($user)-&gt;isPasswordValid($user-&gt;getPassword(), $presentedPassword, $user-&gt;getSalt())) {
              throw new BadCredentialsException('The presented password is invalid.');
          }
      }
  }
  
  /**
   * {@inheritdoc}
   */
  protected function retrieveUser($username, MyToken $token)
  {
      $user = $token-&gt;getUser();
      if ($user instanceof UserInterface) {
          return $user;
      }
  
      try {
          $user = $this-&gt;userProvider-&gt;loadUserByUsername($username);
  
          if (!$user instanceof UserInterface) {
              throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
          }
  
          return $user;
      } catch (UsernameNotFoundException $notFound) {
          $notFound-&gt;setUsername($username);
          throw $notFound;
      } catch (\Exception $repositoryProblem) {
          $ex = new AuthenticationServiceException($repositoryProblem-&gt;getMessage(), 0, $repositoryProblem);
          $ex-&gt;setToken($token);
          throw $ex;
      }
  }
  }
  </code></pre>
  
  <p>MyFactory.php</p>
  
  <pre><code>&lt;?php
  
  
  namespace Test\SampleBundle\DependencyInjection\Security\Factory;
  use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  use Symfony\Component\DependencyInjection\DefinitionDecorator;
  use Symfony\Component\DependencyInjection\ContainerBuilder;
  use Symfony\Component\DependencyInjection\Reference;
  use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
  
  class MyFactory implements SecurityFactoryInterface
  {
  
  public function __construct()
  {
      $this-&gt;addOption('username_parameter', '_username');
      $this-&gt;addOption('password_parameter', '_password');
      $this-&gt;addOption('csrf_parameter', '_csrf_token');
      $this-&gt;addOption('intention', 'authenticate');
      $this-&gt;addOption('post_only', true);
  }
  
    public function getPosition()
  {
      return 'pre_auth';
  }
    public function getKey()
  {
      return 'mylogin';
  }
  
  
  
  
  public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
  {
      $providerId = 'security.authentication.provider.mylogin.'.$id;
      $container
          -&gt;setDefinition($providerId, new DefinitionDecorator('mylogin.security.authentication.provider'))
          -&gt;replaceArgument(0, new Reference($userProvider))
      ;
  
      $listenerId = 'security.authentication.listener.mylogin.'.$id;
      $listener = $container-&gt;setDefinition($listenerId, new DefinitionDecorator('mylogin.security.authentication.listener'));
  
      return array($providerId, $listenerId, $defaultEntryPoint);
  }
  
     public function addConfiguration(NodeDefinition $node)
  {
  }
  
  
  protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
  {
      $entryPointId = 'security.authentication.form_entry_point.'.$id;
      $container
          -&gt;setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point'))
          -&gt;addArgument(new Reference('security.http_utils'))
          -&gt;addArgument($config['login_path'])
          -&gt;addArgument($config['use_forward'])
      ;
  
      return $entryPointId;
  }
      final public function addOption($name, $default = null)
  {
      $this-&gt;options[$name] = $default;
  }
  }
  </code></pre>
  
  <p>Services.yml:</p>
  
  <pre><code>services:
  
  mylogin.security.authentication.provider:
  class:  Test\SampleBundle\Security\Authentication\Provider\MyProvider
  arguments: ['', %kernel.cache_dir%/security/nonces]
  
  mylogin.security.authentication.listener:
  class:  Test\SampleBundle\Security\Firewall\MyListener
  arguments: [@security.context, @security.authentication.manager]
  </code></pre>
  
  <p>And finally injector:</p>
  
  <pre><code>namespace Test\SampleBundle;
  
  use Test\SampleBundle\DependencyInjection\Security\Factory\MyFactory;
  use Symfony\Component\HttpKernel\Bundle\Bundle;
  use Symfony\Component\DependencyInjection\ContainerBuilder;
  
  class TestSampleBundle extends Bundle
  {
  public function build(ContainerBuilder $container)
  {
      parent::build($container);
  
      $extension = $container-&gt;getExtension('security');
      $extension-&gt;addSecurityListenerFactory(new MyFactory());
  }
  }
  </code></pre>
  
  <p>There may be a few errors in the above code as I have been playing around with stuff to get it working</p>