2
votes

I'm trying to implement user authentication based on 3 fields ( a string, a boolean value and an email ). Following Symfony2 and Fos documentation I've started to create a custom User Provider, but it seems that the interface I have to implement ( UserProviderInterface ) at lower level needs the "loadUserByUsername" method.

My question is: Which is the best way to implement login with multiple fields? Thank you very much!

2
You can think about loadUserByUsername as a method to get a user by an unique identifier. Do you have an unique field for you users ? - Hpatoio
Yes, the id, in db. What I logically consider unique is the combination of the three fields I mentioned. A triplet identifies a single user - Enrike1983
So 2 users can have the same email AND the same value for the "string" ? - Hpatoio
exactly, it can be different just the third value and in that case it would be a different user - Enrike1983
and the user also need to insert password in the login form ? - Hpatoio

2 Answers

1
votes

Assuming you are using FOSUserBundle and that you don't use the username field I think the easier way is to "compose" your 3 fields and use them as username

To do this you need:

  • Customize the login form. Hide the username field and add your 3.
  • Create a kernel.request listener that read your 3 fields and add the username fields to the request object. Once you have done this you are in the "normal" flow

Let's say I've these data:

In the DB my username would be [email protected]|MyRandomString|1 (You can compose the username as you like)

Here an example for the listener

My\PersonBundle\Listener\MyLoginFieldFormatterListener

namespace My\AwesomeBundle\Listener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class MyLoginFieldFormatterListener {

    public function onKernelRequest(GetResponseEvent $event)
    {
        // This is you request object.
        // http://api.symfony.com/2.5/Symfony/Component/HttpFoundation/Request.html
        $request   = $event->getRequest();

        // Add a condition to execute code only when route login data are submitted is required
        if ($request->get('_route') != 'fos_user_security_check')
                return;

        // You can access POST values via `request` property
        $yourField1 = $request->request->get('yourField1');

        // Get the other fields and compose your username

        // The field name MUST be _username as long as you didn't overwrite it in the conf
        $request->request->set('_username', $yourComposedUsername);

    }

}

And the configuration

services.yml

services:
  listener.compose_username_listener:
    class: My\PersonBundle\Listener\MyLoginFieldFormatterListener
    tags:
      - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 255 }

Do not forget priority so this listener is the first one executed.

0
votes

@Hpatoio thank you, your solution works perfectly :) there's only a detail that has to be fixed: listener priority must be set at 31

according to documentation (http://symfony.com/doc/current/reference/dic_tags.html#kernel-request), RouteListener has priority 32

the custom listener has to be executed after RouteListener (to have the correct routing info) and before the validation system

so the correct configuration is:

services:
  listener.compose_username_listener:
    class: My\PersonBundle\Listener\MyLoginFieldFormatterListener
    tags:
      - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 31 }