We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Facebook php sdk version 4

Hi, I'm currently trying to integrate the lastest Facebook php sdk into a Phalcon project but I'm not having much luck. I can get the SDK to work in a standalone project but the exact same code fails when integrated into a Phalcon project (either as a service or directly in a Controller). This issue seems to be that the facebook redirect helper creates a "state" property which is appended to a loginUrl and then stored in a session. When a user is redirected back to my site after signing in, it checks this property against a querystring value. The state property is only generated and stored whenever you display the login url. Somehow, when I integrate this in Phalcon the session variable and the $_GET parameter never seem to match up. The simple example which works is as follows

         // lots of requires
         Facebook\FacebookSession::setDefaultApplication($appId,$secret);
         $helper = new Facebook\FacebookRedirectLoginHelper('https://'.$_SERVER['HTTP_HOST'] .'/');
         // see if a existing session exists
         if ( isset( $_SESSION ) && isset( $_SESSION['fb_token'] ) ) {
          // create new session from saved access_token
              $session = new FacebookSession( $_SESSION['fb_token'] );

          // validate the access_token to make sure it's still valid
          try {
                 if ( !$session->validate() ) {
                 $session = null;
           }
          } catch ( Exception $e ) {
            // catch any exceptions
                $session = null;
            }
           }  // end if isset($_SESSION)

         if ( !isset( $session ) || $session === null ) {
          // no session exists

          try {
                  $session = $helper->getSessionFromRedirect();
           } catch( FacebookRequestException $ex ) {
            // When Facebook returns an error
             // handle this better in production code
              print_r( $ex );
            } catch( Exception $ex ) {
            // When validation fails or other local issues
             // handle this better in production code
               print_r( $ex );
                  }
               }

           // see if we have a session
           if ( isset( $session ) ) {

            // save the session
             $_SESSION['fb_token'] = $session->getToken();
             // create a session using saved token or the new one we generated at login
               $session = new FacebookSession( $session->getToken() );

                // graph api request for user data
                $request = new FacebookRequest( $session, 'GET', '/me' );
                 $response = $request->execute();
                  // get response
                $graphObject = $response->getGraphObject()->asArray();

                 // print profile data
                    echo '<pre>' . print_r( $graphObject, 1 ) . '</pre>';

                      // print logout url using session and redirect_uri (logout.php page should destroy the session)
                       echo '<a href="' . $helper->getLogoutUrl( $session, 'https://yourwebsite.com/app/logout.php' ) . '">Logout</a>';
              } else {
            // show login url
              echo '<a href="' . $helper->getLoginUrl( array( 'email', 'user_friends' ) ) . '">Login</a>'; // this line would generate a new state
             }

When I try using this exact same code in a controller in a phalcon project, the state check always fails even though I'm not generateing a new login url. The only other difference is that in the simple project I require all the facebook files using require_once but in the Phalcon project I use

      $loader->registerNamespaces(
    array(
       "Facebook"    => __DIR__ . '/../../vendor/facebook/php-sdk-v4/src/Facebook/'

    )
   );

Anyone got any clues?

P.S on a side note, do you think the "Help" link on these create post pages could be made to open a new window. It's frustrating typing out a message, tapping help to get some formatting hints and then lose all you've written. I know I can r-click and open in a new tab, but I can't be the 1st one to make this mistake first time round



98.9k

You can add a events manager to the loader to see what files are checked:

https://docs.phalcon.io/en/latest/reference/loader.html#autoloading-events



12.2k
edited Jul '14

The loader doesn't seem to list any of the Facebook files in it in the eventManager, but they do actually work, e.g if I call a method on one of Facebook objects it is fired correctly, which implies it is finding the classes. The issue seems to be around sessions. It's almost like they're "resetting" every time and this is what is causing the issue

edit saying that the loader does list the Facebook objects when I move it back into my services.php file rather than trying to access it in the controller. The fact remains though that it's something to do with the facebook "state" property and sessions.

Specifically the more I add debug_print_backtrace() etc. throughout my code, I can indeed confirm that the Facebook SDK only saves the session once, when it is meant to. Whenever I try to sign in however, the session seems to have changed. And this only happens when I integrate the SDK into a Phalcon project. The exact same code works fine without Phalcon. I'm assuming Phalcon wouldn't be doing some hashin/encryption of sessions objects? These sessions are added via $_SESSION rather than $di->session->set(); but I assume this shouldn't be an issue?



12.2k
Accepted
answer

I've worked this one out. The issue was that the browser was automatically making a request to /favicon.ico as well,as I didn't have a favicon.ico this then rendered the default indexAction again and as such this was causing the getLoginUrl() method to fire again generating a new state. The simple fix is to just create a favicon, or define the error handling route for files not there (I was just using the boilerplate from the phalcon dev tools initially)

Hi @TommyBs The solution is not working for me. I have added a favicon too but it is still with same status.

Fatal error: Uncaught exception 'Facebook\Exceptions\FacebookSDKException' with message 'Cross-site request forgery validation failed. Required param "state" missing.' in /var/www/html/fb_test/src/Facebook/Helpers/FacebookRedirectLoginHelper.php:241 Stack trace: #0 /var/www/html/fb_test/src/Facebook/Helpers/FacebookRedirectLoginHelper.php(221): Facebook\Helpers\FacebookRedirectLoginHelper->validateCsrf() #1 /var/www/html/fb_test/login.php(12): Facebook\Helpers\FacebookRedirectLoginHelper->getAccessToken() #2 {main} thrown in /var/www/html/fb_test/src/Facebook/Helpers/FacebookRedirectLoginHelper.php on line 241

Can you please help me to out this?



12.2k
edited Jan '16

Hi,

Apologies for the late reply, I've just updated a test app to the latest version of the php-sdk and I've had to play around to get this working. Below is the code I use, but you obviously might need to tweak it to your needs:


    //Start the session the first time some component request the session service
    $di->set('session', function () {
    $session = new SessionAdapter();
    $session->start();

    return $session;
    },true);

    $di->get('session'); // This is important, you need to get the session

    /** 
    *   Setup the Facebook authentication
    */
    $di->set('fb', function() use($config){
    return new Facebook\Facebook([
     'app_id' => $config->facebook->app_id,
    'app_secret' => $config->facebook->app_secret,
    'default_graph_version' => $config->facebook->default_graph_version
     ]);
    });

    /**
    * Check for the session before generating a loginURL as the generation will reset the CSRF state
    */
    $di->set('FacebookSession', function() use($di){
    $helper = $di->get('fb')->getRedirectLoginHelper();
    try {
     $accessToken = $helper->getAccessToken();
    } catch(Facebook\Exceptions\FacebookResponseException $e) {
    // When Graph returns an error
     echo 'Graph returned an error: ' . $e->getMessage();
     // exit;
    } catch(Facebook\Exceptions\FacebookSDKException $e) {
     // When validation fails or other local issues
    echo 'Facebook SDK returned an error: ' . $e->getMessage();
    //exit;
    }
    if (isset($accessToken)) {
     // Logged in!
    $_SESSION['facebook_access_token'] = (string) $accessToken;

     // Now you can redirect to another page and use the
     // access token from $_SESSION['facebook_access_token']
    }
    echo $accessToken;
    });

    $di->get('FacebookSession'); // again it's important to call this 

    $di->set('loginUrl', function() use ($di){
    $helper = $di->get('fb')->getRedirectLoginHelper(); 
    try {
        $accessToken = $helper->getAccessToken();
    } catch (Exception $e){ // I only show the login url if their isn't a valid access token

        $permissions = ['user_likes']; // optional
        $loginUrl = $helper->getLoginUrl('https://'.$_SERVER['HTTP_HOST']. strtok($_SERVER['REQUEST_URI'],'?'), $permissions);
        return $loginUrl;
    }
    });