De plus en plus de sites souhaitent intégrer une connexion via Facebook pour faciliter l’inscription de l’utilisateur. Le mieux est d’utiliser un facebook bundle adapté.
Sur Symfony2, FOSFacebookBundle, combiné à FOSUserBundle, rempli parfaitement son rôle.
Les documentations de ces bundles sont très complètes, mais malgré ça, pas mal de personnes se retrouvent très vite perdus. Je vais donc vous exposer ma façon de faire pas à pas. N’hésitez pas à commenter l’article si vous avez des compléments à apporter ! 😉
Dans cet article nous travaillerons sur Symfony2.1, Doctrine et composer pour la gestion des dépendances.
Configuration de FOSUserBundle
Je vais être assez rapide sur cette étape, la documentation vous fourni déjà toutes les informations nécessaires, je m’attarde sur les parties que nous réutiliseront par la suite.
Pour l’installation du bundle, suivez les étapes de cette page.
Une fois le bundle installé, nous allons faire hériter notre UserBundle par celui de FOS :
// src/My/UserBundle/MyUserBundle.php <?php namespace My\UserBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; class MyUserBundle extends Bundle { public function getParent() { return 'FOSUserBundle'; } }
Cela nous permettra plus tard de modifier facilement le template de connexion pour ajouter notre facebook connect.
Création d’une application sur Facebook
Avant de continuer le développement sur Symfony, nous avons d’abord besoin d’avoir une application Facebook pour mettre en place notre FacebookConnect.
Pour ce faire, rendez-vous sur http://developers.facebook.com/ et authentifiez-vous.
Allez ensuite dans Applications, puis cliquez sur Créer une application, vous devriez voir apparaître un formulaire, remplissez-le de cette façon :
Validez, vous devriez tomber sur la page des paramètres principaux de votre nouvelle application.
Ce n’est pas terminé, il reste encore quelques informations à compléter ! 🙂
Pour que votre application fonctionne sur votre site, vous devez spécifier les domaines à utiliser pour l’authentification Facebook dans le champs AppDomains.
Dans notre cas nous en mettrons qu’un seul, monsupersiteweb.fr qui permettra l’authentification facebook sur tous les sous-domaines possibles.
Ensuite, vous devez activer le Facebook Login, pour cela cliquez sur Website with Facebook Login et tapez l’adresse de base de votre site web sur le champ apparaissant. Dans notre cas il s’agit de http://monsupersiteweb.fr/.
Au final, votre page de paramètres devrait ressembler à ça :
N’oubliez pas d’enregistrer les modifications ! 😛
Pour le reste des paramètres, je vous laisse découvrir leurs fonctionnements en consultant la documentation de Facebook ! 🙂
Attention pour les sites en local ! Préférez une adresse comme local.monsupersiteweb.fr ou dev.monsupersiteweb.fr pour que l’api Facebook fonctionne correctement !
Installation de FOSFacebookBundle
La méthode d’installation de FOSFacebookBundle va globalement suivre celle de la documentation sur github, je la ré-explique pour suivre avec l’application que nous venons de créer.
Commencez par la méthode habituelle, l’ajout du bundle via composer.json :
php composer.phar require friendsofsymfony/facebook-bundle # dev-master
Puis son ajout dans le fichier ApplicationKernel.php :
// app/ApplicationKernel.php public function registerBundles() { return array( // ... new FOS\FacebookBundle\FOSFacebookBundle(), // ... ); }
Ensuite, dans le fichier de configuration routing.yml, ajoutons les routes nécessaires :
# app/config/routing.yml _security_check: pattern: /login_facebook_check _security_logout: pattern: /logout
Configuration de l’application Facebook
Avant de continuer, ajoutons quelques informations concernant l’application dans notre fichier parameters.yml, cela peut être pratique si elles viennent a être modifiées dans le futur.
# app/config/parameters.yml api_facebook_id: 532147190133967 # ID de l’application/Clé de l’API api_facebook_secret: 7c43d4bca55690b16da9329be0d60ad2 # Clé secrète api_facebook_name: http://apps.facebook.com/mon-super-site-web/ # App Namespace api_facebook_server: http://monsupersiteweb.fr/ # URL du site
Les informations ci-dessus correspondent aux paramètres que l’on a vu précédemment les commentaires vous permettrons de les retrouver facilement ! 😉
A noter pour les petits rigolos qui tenterons d’utiliser les codes de cette application, celle-ci n’existe plus ! 😛
Maintenant, importons les configurations requires du bundle dans config.yml :
# app/config/config.yml fos_facebook: file: %kernel.root_dir%/../vendor/facebook/php-sdk/src/base_facebook.php # Emplacement du SDK Facebook, auto-installé lors de l'import du bundle alias: facebook app_id: %api_facebook_id% secret: %api_facebook_secret% cookie: true permissions: [email, user_birthday, user_location]
A vous de changer les permissions, selon les informations que vous souhaitez récupérer, mais ne soyez pas trop gourmands ! 🙂
Attention ! Si vous utilisez la version 1.1.x, le paramètre file n’est plus utilisé, ne le mettez pas. Merci à Pyrrah pour l’astuce ! 🙂
Pour terminer, configurons notre fichier security.yml :
# app/config/security.yml security: providers: fos_userbundle: id: fos_user.user_provider.username my_facebook_provider: id: my_user.facebook_provider # Notre provider, nous y viendrons par la suite. firewalls: main: pattern: ^/ form_login: provider: fos_userbundle csrf_provider: form.csrf_provider login_path: /login check_path: /login_check fos_facebook: app_url: "%api_facebook_name%" server_url: "%api_facebook_server%" check_path: /login_facebook_check # Pour éviter toute confusion avec le formulaire de base, j'ai spécifié une route pour Facebook provider: my_facebook_provider anonymous: true logout: path: /logout handlers: ["fos_facebook.logout_handler"]
Création du User UserProvider
Dans ce tutoriel, nous choisirons de créer notre propre UserProvider nous permettant de contrôler la façon de traiter les données Facebook lors de l’authentification.
Commençons d’abord par adapter notre entité User. Nous avons besoin d’une information principale, sont facebook_id, nous permettant de le retrouver lors de sa prochaine authentification via Facebook. Voici une classe Doctrine d’exemple inspirée de la doc officielle :
// src/My/UserBundle/Entity/User.php <?php namespace My\UserBundle\Entity; use FOS\UserBundle\Entity\User as BaseUser; use Doctrine\ORM\Mapping as ORM; class User extends BaseUser { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="firstname", type="string", length=255) */ protected $firstname; /** * @var string * * @ORM\Column(name="lastname", type="string", length=255) */ protected $lastname; /** * @var string * * @ORM\Column(name="facebookId", type="string", length=255) */ protected $facebookId; /** * @return int */ public function getId() { return $this->id; } /** * @return string */ public function getFirstname() { return $this->firstname; } /** * @param string $firstname */ public function setFirstname($firstname) { $this->firstname = $firstname; } /** * @return string */ public function getLastname() { return $this->lastname; } /** * @param string $lastname */ public function setLastname($lastname) { $this->lastname = $lastname; } /** * Get the full name of the user (first + last name) * @return string */ public function getFullName() { return $this->getFirstName() . ' ' . $this->getLastname(); } /** * @param string $facebookId * @return void */ public function setFacebookId($facebookId) { $this->facebookId = $facebookId; $this->setUsername($facebookId); $this->salt = ''; } /** * @return string */ public function getFacebookId() { return $this->facebookId; } /** * @param Array */ public function setFBData($fbdata) // C'est dans cette méthode que vous ajouterez vos informations { if (isset($fbdata['id'])) { $this->setFacebookId($fbdata['id']); $this->addRole('ROLE_FACEBOOK'); } if (isset($fbdata['first_name'])) { $this->setFirstname($fbdata['first_name']); } if (isset($fbdata['last_name'])) { $this->setLastname($fbdata['last_name']); } if (isset($fbdata['email'])) { $this->setEmail($fbdata['email']); } } }
Pour plus d’infos sur les données récupérables, allez faire un tour sur la doc concernant le Graph API de Facebook.
Une fois l’entité User préparée, nous allons mettre en place notre UserProvider qui va nous permettre de renvoyer ou de créer l’utilisateur de votre site une fois la connexion Facebook faite.
La classe d’exemple qui va suivre est globalement la même que celle de la doc officielle :
// src/My/UserBundle/Security/User/Provider/FacebookProvider <?php namespace My\UserBundle\Security\User\Provider; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserInterface; use \BaseFacebook; use \FacebookApiException; class FacebookProvider implements UserProviderInterface { /** * @var \Facebook */ protected $facebook; protected $userManager; protected $validator; public function __construct(BaseFacebook $facebook, $userManager, $validator) { $this->facebook = $facebook; $this->userManager = $userManager; $this->validator = $validator; } public function supportsClass($class) { return $this->userManager->supportsClass($class); } public function findUserByFbId($fbId) { return $this->userManager->findUserBy(array('facebookId' => $fbId)); } public function loadUserByUsername($username) { $user = $this->findUserByFbId($username); try { $fbdata = $this->facebook->api('/me'); } catch (FacebookApiException $e) { throw new UsernameNotFoundException('The user is not authenticated on facebook'); $fbdata = null; } if (!empty($fbdata)) { if (empty($user)) { $user = $this->userManager->createUser(); $user->setEnabled(true); $user->setPassword(''); $user->setFBData($fbdata); // Ici on passe les données Facebook à notre classe User afin de la mettre à jour } if (count($this->validator->validate($user, 'Facebook'))) { throw new UsernameNotFoundException('The facebook user could not be stored'); } $this->userManager->updateUser($user); } if (empty($user)) { throw new UsernameNotFoundException('The user is not authenticated on facebook'); } return $user; } public function refreshUser(UserInterface $user) { if (!$this->supportsClass(get_class($user)) || !$user->getFacebookId()) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); } return $this->loadUserByUsername($user->getFacebookId()); } }
Déclarons ce provider en tant que service :
<!-- Resources/config/services.xml --> <parameters> <parameter key="my_user.facebook_provider.class">My\UserBundle\Security\User\Provider\FacebookProvider</parameter> </parameters> <services> <!-- Facebook provider --> <service id="my_user.facebook_provider" class="%my_user.facebook_provider.class%"> <argument type="service" id="fos_facebook.api" /> <argument type="service" id="fos_user.user_manager" /> <argument type="service" id="validator" /> <argument type="service" id="service_container" /> <argument>%fos_user.model.user.class%</argument> </service> </services>
Si vous êtes curieux, vous pouvez allez voir à quoi sert un UserProvider ici. 😉
Intégration de Facebook sur le layout
Voilà c’est tout pour l’arrière plan, vous suivez encore ? Allez il ne reste plus qu’à mettre le bouton FBConnect ! 😀
Commençons par inclure le script Facebook nécessaire :
<!-- app/Resources/views/base.html.twig --> <html xmlns:fb="http://www.facebook.com/2008/fbml"> <body> {{ facebook_initialize({'xfbml': true, 'culture': 'fr_FR', 'fbAsyncInit': 'onFbInit();'}) }} <script type="text/javascript" > // Script for FOSFacebookBundle function goLogIn(){ window.location = "{{ path('_security_check') }}"; } function onFbInit() { if (typeof(FB) != 'undefined' && FB != null ) { FB.Event.subscribe('auth.statusChange', function(response) { if (response.session || response.authResponse) { setTimeout(goLogIn, 50); } else { window.location = "{{ path('_security_logout') }}"; } }); } } </script> </body> </html>
Et vous pouvez ensuite intégrer le bouton facebook ou vous le souhaitez en utilisant ce code :
{{ facebook_login_button({'autologoutlink': false}) }}
Note : autologoutlink est à false car la déconnexion n’est pas nécessaire via Facebook pour cette méthode.
Conclusion
A ce stade vous devriez pouvoir à la fois vous connecter de façon classique via le formulaire, et faire un Facebook Connect sans soucis ! 🙂
L’intérêt de cette méthode, c’est qu’elle vous permet de rester détaché de l’application Facebook.
Elle ne sert ici que de passerelle pour récupérer les informations et authentifier l’utilisateur.
Par exemple, si l’utilisateur se déconnecte manuellement, cela ne le virera pas de son compte Facebook! 😀
Ce qui est le cas malheureusement sur certains sites…
J’espère que cette article, bien qu’un peu long, vous facilitera la tache, si vous rencontrez un problème en suivant cette méthode, n’hésitez pas à m’en faire part par commentaires ! 😉
Pingback: FOSFacebookBundle & FOSUserBundle : Install...()