Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Introduction

This Bundle enables integration of the Facebook PHP and JS SDK's. Furthermore it also provides a Symfony2 authentication provider so that users can login to a Symfony2 application via Facebook. Furthermore via custom user provider support the Facebook login can also be integrated with other data sources like the database based solution provided by FOSUserBundle.

Note that logging in a user requires multiple steps:

  1. the user must be logged into Facebook
  2. the user must connect his Facebook account to your site
  3. once the user has done 1. and 2. you must trigger the login

For step 1. and 2. there are two options:

  1. Select "OAuth Migration" in the Facebook application settings.
  2. Add a Facebook login button, this approach requires JS code to handle step 3.
  3. Letting the FOSFacebookBundle redirecting to the Facebook login page

Note that the later happens automatically if the first provider in your first firewall configuration is configured to FOSFacebookBundle and the user accesses a page that requires authentication without being authenticated.

Please also refer to the Facebook documentation: https://developers.facebook.com/docs/guides/web/

Please also refer to the official documentation of the SecurityBundle, especially for details on the configuration: http://symfony.com/doc/2.0/book/security/authentication.html

Build Status

Installation

  1. Add this bundle and the Facebook PHP SDK to your vendor/ dir:

    • Using the vendors script.

      Add the following lines in your deps file::

      [FOSFacebookBundle]
      git=git://github.com/FriendsOfSymfony/FOSFacebookBundle.git
      target=/bundles/FOS/FacebookBundle
      [FacebookSDK]
      git=git://github.com/facebook/php-sdk.git
      target=/facebook
      

      Run the vendors script:

      ./bin/vendors install
      
    • Using git submodules.

      $ git submodule add git://github.com/FriendsOfSymfony/FOSFacebookBundle.git vendor/bundles/FOS/FacebookBundle
      $ git submodule add git://github.com/facebook/php-sdk.git vendor/facebook
      
  2. Add the FOS namespace to your autoloader:

     // app/autoload.php
    $loader->registerNamespaces(array(
    'FOS' => __DIR__.'/../vendor/bundles',
    // your other namespaces
    ));
    
  3. Add this bundle to your application's kernel:

     // app/ApplicationKernel.php
    public function registerBundles()
    {
    return array(
    // ...
    new FOS\FacebookBundle\FOSFacebookBundle(),
    // ...
    );
    }
    
  4. Add the following routes to your application and point them at actual controller actions

     #application/config/routing.yml
    _security_check:
    pattern: /login_check
    _security_logout:
    pattern: /logout
    #application/config/routing.xml
    <route id="_security_check" pattern="/login_check" />
    <route id="_security_logout" pattern="/logout" /> 
  5. Configure the facebook service in your config:

     # application/config/config.yml
    fos_facebook:
    file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
    alias: facebook
    app_id: 123456879
    secret: s3cr3t
    cookie: true
    permissions: [email, user_birthday, user_location]
    # application/config/config.xml
    <fos_facebook:api
    file="%kernel.root_dir%/../vendor/facebook/src/base_facebook.php"
    alias="facebook"
    app_id="123456879"
    secret="s3cr3t"
    cookie="true"
    >
    <permission>email</permission>
    <permission>user_birthday</permission>
    <permission>user_location</permission>
    </fos_facebook:api>
    

    If you do not include a file value in the config you will have to configure your application to autoload the BaseFacebook class.

  6. Add this configuration if you want to use the security component:

     # application/config/config.yml
    security:
    providers:
    fos_facebook:
    id: fos_facebook.auth
    firewalls:
    public:
    # since anonymous is allowed users will not be forced to login
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    access_control:
    - { path: ^/secured/.*, role: [IS_AUTHENTICATED_FULLY] } # This is the route secured with fos_facebook
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    

    You have to add /secured/ in your routing for this to work. An example would be...

     _facebook_secured:
    pattern: /secured/
    defaults: { _controller: AcmeDemoBundle:Welcome:index }
    
  7. Optionally define a custom user provider class and use it as the provider or define path for login

     # application/config/config.yml
    security:
    providers:
    # choose the provider name freely
    my_fos_facebook_provider:
    id: my.facebook.user # see "Example Custom User Provider using the FOS\UserBundle" chapter further down
    firewalls:
    public:
    pattern: ^/.*
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/"
    login_path: ^/login
    check_path: ^/login_check$
    default_target_path: /
    provider: my_fos_facebook_provider
    anonymous: true
    logout:
    handlers: ["fos_facebook.logout_handler"]
    # application/config/config_dev.yml
    security:
    firewalls:
    public:
    fos_facebook:
    app_url: "http://apps.facebook.com/appName/"
    server_url: "http://localhost/facebookApp/app_dev.php/" 
  8. Optionally use access control to secure specific URLs

     # application/config/config.yml
    security:
    # ...
    access_control:
    - { path: ^/facebook/, role: [ROLE_FACEBOOK] }
    - { path: ^/.*, role: [IS_AUTHENTICATED_ANONYMOUSLY] }
    
The role `ROLE_FACEBOOK` has to be added in your User class (see Acme\MyBundle\Entity\User::setFBData() below).
> Note that the order of access control rules matters!

Setting up the JavaScript SDK

A templating helper is included for loading the Facebook JavaScript SDK and initializing it with parameters from your service container. To setup the Facebook JavaScript environment, add the following to your layout just after the opening body tag:

 <body>
<!-- inside a php template -->
<?php echo $view['facebook']->initialize(array('xfbml' => true, 'fbAsyncInit' => 'onFbInit();')) ?>
<!-- inside a twig template -->
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}

Note that fbAsyncInit is a parameter helping you to execute JavaScript within the function initializing the connection with Facebook, just after the FB.init(); call. onFbInit(); is a JavaScript function defined furthermore to execute functions which need FB initialized.

If you will be adding XFBML markup to your site you may also declare the namespace, perhaps in the opening html tag:

 <html xmlns:fb="http://www.facebook.com/2008/fbml">

Include the login button in your templates

Just add the following code in one of your templates:

<!-- inside a php template -->
<?php echo $view['facebook']->loginButton(array('autologoutlink' => true)) ?>
<!-- inside a twig template -->
{{ facebook_login_button({'autologoutlink': true}) }}

Note that with this approach only the login and connecting with Facebook will be handled. The step of logging in the user into your Symfony2 application still needs to be triggered. To do this you will in most cases simply subscribe to the "auth.login" event and then redirect to the "check_path":

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
}
}
</script>

Note that we wait 500ms before redirecting to let the browser dealing with the Facebook cookie. You can avoid this step but you might get this error message: "The Facebook user could not be retrieved from the session."

The "_security_check" route would need to point to a "/login_check" pattern to match the above configuration.

Also, you need to trigger the logout action, so, subscribe the "auth.logout" to redirect to the "logout" route:

<script>
function goLogIn(){
window.location = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.login', function(response) {
setTimeout(goLogIn,500);
});
FB.Event.subscribe('auth.logout', function(response) {
window.location = "{{ path('_security_logout') }}";
});
}
}
</script>

Example Custom User Provider using the FOS\UserBundle

This requires adding a service for the custom user provider which is then set to the provider id in the "provider" section in the config.yml:

services:
my.facebook.user:
class: Acme\MyBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "@fos_facebook.api"
userManager: "@fos_user.user_manager"
validator: "@validator"
container: "@service_container"
<?php
namespace Acme\MyBundle\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) {
$fbdata = null;
}
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
$user->setAlgorithm('');
}
// TODO use http://developers.facebook.com/docs/api/realtime
$user->setFBData($fbdata);
if (count($this->validator->validate($user, 'Facebook'))) {
// TODO: the user was found obviously, but doesnt match our expectations, do something smart
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());
}
}

Finally one also needs to add a getFacebookId() and setFBData() method to the User model. The following example also adds "firstname" and "lastname" properties, using the Doctrine ORM:

<?php
namespace Acme\MyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
class User extends BaseUser
{
/**
* @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;
public function serialize()
{
return serialize(array($this->facebookId, parent::serialize()));
}
public function unserialize($data)
{
list($this->facebookId, $parentData) = unserialize($data);
parent::unserialize($parentData);
}
/**
* @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)
{
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']);
}
}
}

About

Integrate the Facebook Platform into your Symfony2 application.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors