Tutos & Astuces

FOSFacebookBundle & FOSUserBundle : Installation et configuration pas à pas

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...

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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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 :

Facebook - Popup de création d'une application

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 :

Facebook - paramètres principaux d'une application

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 :

1
php composer.phar require friendsofsymfony/facebook-bundle # dev-master

Puis son ajout dans le fichier ApplicationKernel.php :

1
2
3
4
5
6
7
8
9
// 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 :

1
2
3
4
5
# 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.

1
2
3
4
5
# 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 :

1
2
3
4
5
6
7
8
# 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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// 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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- app/Resources/views/base.html.twig -->
    <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 :

1
{{ 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 ! 😉

Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0