December 11, 2013

December 11, 2013
1
In this article, we are going to discuss about How to intergate the Facebook connect with Cakephp Auth component. By connecting Facebook connect with cakephp Auth component, we can allow both normal user account users and Facebook connect generated users can access the application. we can achieve by dynamically set the Auth->fields.

Follow the below steps to integrate the Facebook connect with the cakephp Auth component.

Step 1 :

Delete the Facebook client libraries from "app/vendors/facebook" folder. In your "app_controller", import the classes, and set the below properties.

<?php
App::import('Vendor', 'facebook/facebook');
class AppController extends Controller {
    var $components = array('Auth');
    var $uses = array('User');
    var $facebook;
    var $__fbApiKey = 'your_key';
    var $__fbSecret = 'your_secret';
}

Step 2 : 

To instantiate a Facebook Object, overwrite the inherited "__construct" method like below.

function __construct() {
    parent::__construct();
        
    // Prevent the 'Undefined index: facebook_config' notice from being thrown.
    $GLOBALS['facebook_config']['debug'] = NULL;
        
    // Create a Facebook client API object.
    $this->facebook = new Facebook($this->__fbApiKey, $this->__fbSecret);

  }

Step 3 :


In our "beforeFiltermethod, we define the default Auth properties, call a private __checkFBStatus method, and pass any user data to the view:

function beforeFilter() {
    // Authentication settings
    $this->Auth->fields = array('username' => 'email', 'password' => 'password');
    $this->Auth->logoutRedirect = '/';

    //check to see if user is signed in with facebook
    $this->__checkFBStatus();

    //send all user info to the view
    $this->set('user', $this->Auth->user());
}

Step 4 :


Define the "private method __checkFBStatus" that's called in our "beforeFilter" method. Before that, we need check to make sure "$this->Auth->User" isn't already set by a normal user account, and we check to see if there's a logged in Facebook user. The logged in facebook user is available after we use the JavaScript API to log facebook users into the site.

private function __checkFBStatus(){
        //check to see if a user is not logged in, but a facebook user_id is set
        if(!$this->Auth->User() &amp;&amp; $this->facebook->get_loggedin_user()):

Step 5 :

Check if the user is already logged in and also check already has an entry in our User table.

//see if this facebook id is in the User database; if not, create the user using their fbid hashed as their password
$user_record =
    $this->User->find('first', array(
        'conditions' => array('fbid' => $this->facebook->get_loggedin_user()),
        'fields' => array('User.fbid', 'User.fbpassword', 'User.password'),
        'contain' => array()
    ));

Step 6:

If no record was found, we create a new record for this user. We are setting 3 variables, fbid, fbpassword, and password. The fbpassword field will hold a randomly generated 20 character string un-hashed so that we can access the value of the field directly from the database. We retrieve this value based on the fbid field, hash it, and that's our password as the Auth Component expects:

//create new user
if(empty($user_record)):
    $user_record['fbid'] = $this->facebook->get_loggedin_user();
    $user_record['fbpassword'] = $this->__randomString();
    $user_record['password'] = $this->Auth->password($user_record['fbpassword']);
    
    $this->User->create();
    $this->User->save($user_record);
endif;

Step 7:

We need to then update our Auth Component's fields property to use fbid as the username.

//change the Auth fields
    $this->Auth->fields = array('username' => 'fbid', 'password' => 'password');

    //log in the user with facebook credentials
    $this->Auth->login($user_record);
    
endif;
}

Step 8:

Here's the complete app_controller.php:

<?php
App::import('Vendor', 'facebook/facebook');

class AppController extends Controller {
    var $components = array('Auth');
    var $uses = array('User');
  var $facebook;
  var $__fbApiKey = 'your_key';
  var $__fbSecret = 'your_secret';
    
  function __construct() {
    parent::__construct();
        
    // Prevent the 'Undefined index: facebook_config' notice from being thrown.
    $GLOBALS['facebook_config']['debug'] = NULL;
        
    // Create a Facebook client API object.
    $this->facebook = new Facebook($this->__fbApiKey, $this->__fbSecret);
  }

    function beforeFilter() {
    // Authentication settings
    $this->Auth->fields = array('username' => 'email', 'password' => 'password');
    $this->Auth->logoutRedirect = '/';

        //check to see if user is signed in with facebook
        $this->__checkFBStatus();

        //send all user info to the view
        $this->set('user', $this->Auth->user());
  }

    private function __checkFBStatus(){
        //check to see if a user is not logged in, but a facebook user_id is set
        if(!$this->Auth->User() &amp;&amp; $this->facebook->get_loggedin_user()):

            //see if this facebook id is in the User database; if not, create the user using their fbid hashed as their password
            $user_record =
                $this->User->find('first', array(
                    'conditions' => array('fbid' => $this->facebook->get_loggedin_user()),
                    'fields' => array('User.fbid', 'User.fbpassword', 'User.password'),
                    'contain' => array()
                ));

            //create new user
            if(empty($user_record)):
                $user_record['fbid'] = $this->facebook->get_loggedin_user();
                $user_record['fbpassword'] = $this->__randomString();
                $user_record['password'] = $this->Auth->password($user_record['fbpassword']);
                
                $this->User->create();
                $this->User->save($user_record);
            endif;

            //change the Auth fields
            $this->Auth->fields = array('username' => 'fbid', 'password' => 'password');

            //log in the user with facebook credentials
            $this->Auth->login($user_record);
            
        endif;
    }

    private function __randomString($minlength = 20, $maxlength = 20, $useupper = true, $usespecial = false, $usenumbers = true){
        $charset = "abcdefghijklmnopqrstuvwxyz";
        if ($useupper) $charset .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        if ($usenumbers) $charset .= "0123456789";
        if ($usespecial) $charset .= "~@#$%^*()_+-={}|][";
        if ($minlength > $maxlength) $length = mt_rand ($maxlength, $minlength);
        else $length = mt_rand ($minlength, $maxlength);
        $key = '';
        for ($i=0; $i<$length; $i++){
            $key .= $charset[(mt_rand(0,(strlen($charset)-1)))];
        }
        return $key;
    }

}
?>

Step 9 :

Next we need to make a few changes to our default.ctp layout; first add the facebook namespace.

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

Step 10:

Add these 2 javascript snippets.

<script type="text/javascript" src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"></script>
<script type="text/javascript">FB.init("your_api_key","/xd_receiver.htm");</script>

Step 11:

Last step, place your login and logout buttons in your default view. Few things to point out here; onlogin="window.location.reload();" will cause the page to reload after a successful Facebook Connect login. This is how we are able to access $this->facebook->get_loggedin_user() in our app_controller to force the manual Auth login. If the user is a facebook user (determined with $user['User']['fbid'] > 0), add an onclick event that calls the FB.Connect.logout() function with a redirect URL as the parameter. This will log the User out of the Auth Component after it logs the user out of Facebook.

<?php
if(!empty($user)):
if($user['User']['fbid'] > 0):
echo $html->link('logout', '#', array('onclick' => 'FB.Connect.logout(function() { document.location = \'http://your_server/users/logout/\'; }); return false;'));
else:
echo $html->link('logout', array('controller' => 'users', 'action' => 'logout'));
endif;
else:
echo '<fb:login-button onlogin="window.location.reload();"></fb:login-button>';
endif;

?>

One last thing to consider, for security reasons… In your login method, be sure only users with fbid = 0 can login via the normal auth fields (username / password). Just an extra precaution considering you have an unhashed password in your database for those facebook users.

1 comments: