[Symfony2][FOSUserBundle] Se connecter via l’adresse mail

Dans le cadre du développement d’un site pour un client, j’ai du adapter FOSUser pour une utilisation sans username. En gros, il fallait que je me connecte en utilisant l’adresse mail 🙂

Première étape de recherche sur internet… (On va pas réinventer la roue)

Sur un site francophone bien connu (le site du zéro) la question fut posée en novembre 2011. Cependant, pour moi, la réponse apportée (j’en ai compris de modifier le getUsername() pour qu’il retourne l’adresse mail) n’était pas satisfaisante.

En abandonnant toute recherche sur des sites francophones (En fait, aucune vraie doc sur le sujet ne semble exister en français), je me suis dirigé vers la doc officielle de FOSUser. où la réponse a été apportée le … 27 novembre. Dommage pour l’internaute français, à une/deux semaines près il avait un tuto 🙂

Pour résumer :

1 ) Déclarer le service

1.1 – Mon DependencyInjection n’existe pas

Si le dossier DependencyInjection n’existe pas dans le bundle, c’est que :

  • Soit vous l’avez supprimé
  • Soit vous n’avez pas généré le bundle via le CLI

Dans tous les cas, s’il n’existe pas, il suffira de le créer (Faire un dossier nommé « DependencyInjection » à la racine du bundle) et d’éditer dedans un fichier nommé comme namespaceExtension.php (par convention), soit pour ma part s4aUserExtension.php, je vous livre le code simple qui va dedans :

  1. <?php
  2.  
  3. namespace s4a\UserBundle\DependencyInjection; //Suivant votre namespace
  4.  
  5. use Symfony\Component\DependencyInjection\ContainerBuilder;
  6. use Symfony\Component\Config\FileLocator;
  7. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  8. use Symfony\Component\DependencyInjection\Loader;
  9.  
  10. class s4aUserExtension extends Extension
  11. {
  12.     public function load(array $configs, ContainerBuilder $container)
  13.     {
  14.         $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  15.         $loader->load('services.yml');
  16.     }
  17. }
<?php

namespace s4a\UserBundle\DependencyInjection; //Suivant votre namespace

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

class s4aUserExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
    }
}

Ce fichier va juste parser le fichier YML services.yml que l’on va créer.

1.2 – Création du service

Ensuite, dans Resources/config il suffit de créer le fichier « services.yml » (Vous pouvez très bien le créer ailleurs ou mettre un autre nom si vous modifiez les lignes 14 et 15 du script plus haut).

Dedans, on déclare le service.

# src/s4a/UserBundle/Resources/config/services.yml
services:
    s4a_user.my_provider:
        class: s4a\UserBundle\Security\Provider\MyProvider #Chemin vers le provider
        public: false
        arguments: ["@fos_user.user_manager"]

Attention, des espaces, pas des tabulations 😉
Le chemin vers le provider indique le répertoire + le nom de la classe du provider que l’on va créer tout de suite.

Dans security.yml, il faut modifier le provider que va chercher Symfony pour prendre en compte notre nouveau service.

security:
    #encoders
    #role_hierarchy
    providers:
        fos_userbundle:
            id: fos_user.user_manager
        custom:
            id: s4a_user.my_provider

2) Le Provider

Il est précisé sur la doc qu’il y a deux méthodes. Celle ci est un peu plus longues mais comporte moins de bugs potentiels, c’est la méthode « propre ».

Il faudra donc créer un fichier ici : Mon/Bundle/Security/Provider (Security et Provider étant des dossiers à créer) nommé « MyProvider.php ».

Son contenu est assez bref et ne nécessite pas tant d’explications :

  1. <?php
  2.  
  3. namespace s4a\UserBundle\Security\Provider; //A changer suivant le nom de votre bundle
  4.  
  5. use FOS\UserBundle\Model\UserManagerInterface;
  6. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  7. use Symfony\Component\Security\Core\User\UserProviderInterface;
  8. use Symfony\Component\Security\Core\User\UserInterface;
  9.  
  10. class MyProvider implements UserProviderInterface
  11. {
  12.     private $userManager;
  13.  
  14.     public function __construct(UserManagerInterface $userManager)
  15.     {
  16.         $this->userManager = $userManager;
  17.     }
  18.  
  19. //On surcharge cette méthode qui se trouve dans FOS/UserBundle/Model/UserManager.php
  20.     public function loadUserByUsername($username)
  21.     {
  22.         $user = $this->userManager->findUserByUsernameOrEmail($username);//On cherche dans les deux
  23.  
  24.         if (!$user) {
  25.             throw new UsernameNotFoundException(sprintf('No user with name "%s" was found.', $username));
  26.         }
  27.  
  28.         return $user;
  29.     }
  30.  
  31.     public function refreshUser(UserInterface $user)
  32.     {
  33.         return $this->userManager->refreshUser($user);
  34.     }
  35.  
  36.     public function supportsClass($class)
  37.     {
  38.         return $this->userManager->supportsClass($class);
  39.     }
  40. }
<?php

namespace s4a\UserBundle\Security\Provider; //A changer suivant le nom de votre bundle

use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class MyProvider implements UserProviderInterface
{
    private $userManager;

    public function __construct(UserManagerInterface $userManager)
    {
        $this->userManager = $userManager;
    }

//On surcharge cette méthode qui se trouve dans FOS/UserBundle/Model/UserManager.php
    public function loadUserByUsername($username)
    {
        $user = $this->userManager->findUserByUsernameOrEmail($username);//On cherche dans les deux

        if (!$user) {
            throw new UsernameNotFoundException(sprintf('No user with name "%s" was found.', $username));
        }

        return $user;
    }

    public function refreshUser(UserInterface $user)
    {
        return $this->userManager->refreshUser($user);
    }

    public function supportsClass($class)
    {
        return $this->userManager->supportsClass($class);
    }
}

3) Et enfin…

On vide le cache, pour recharger les yml, et c’est bon !

  1. Dans le security.yml il manque la liaison entre le nouveau provider et le firewall « main » il faut remplacer fos_userbundle par custom

    firewall:
    main:
    form_login:
    provider: custom

  2. bonjour,

    merci pour votre tuto.
    après avoir suivie votre tuto j’ai eu cette erreur, klk1 pourai m’aider SVP

    Fatal error: Maximum function nesting level of ‘100’ reached, aborting! in C:\wamp\www\Symfony\vendor\symfony\src\Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge.php on line 59

  3. Cela ne fonctionne absolument pas, n’auriez vous pas oublié quelque chose ? Peut être la référence à votre provider dans security.yml ?

Laisser un commentaire


NOTE - Vous pouvez utiliser les éléments et attributs HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" extra="">