Max Power
Max Power

Reputation: 59

When using CodeIgniter + Facebook PHP SDK: getUser() always returns 0

I am attempting to use the Facebook PHP SDK in conjunction with CodeIgniter to allow users to login to my site using Facebook Connect. No matter what I try, getUser() always returns 0, even after (apparently) successful authentication via Facebook.

CodeIgniter version: 2.1.3

Facebook PHP SDK version: 3.2.2

I have created a config file, facebook.php, in the application/config folder and I am loading the Facebook PHP SDK via CodeIgniter's $this->load->library(...) method. The library is indeed getting loaded and I can successfully call many of the get...() methods including getAccessToken(), getAppId() and getAppSecret(), all of which return their expected values.

Here is a stripped down version of my login controller: (note that I also provide an alternate method of logging in via email, hence the CodeIgniter session code sprinkled throughout)

class Login extends CI_Controller {

    public function __construct()
    {
        //Call parent constructor
        parent::__construct();

        //Magic sauce - not sure if this is required but a lot of other people
        //are recommending it to be included (happy to remove it if necessary)
        parse_str($_SERVER['QUERY_STRING'], $_REQUEST);

        //Load facebook library
        $facebook_config = $this->load->config('facebook');
        $this->load->library('facebook', $facebook_config);
    }

   public function index()
    {
        //Check if user is logged in
        $user_id = $this->session->userdata('user_id');
        $is_logged_in = $this->session->userdata('is_logged_in');

        if(($is_logged_in) && ($user_id != 0)) {
            //Logged in - redirect to game
            redirect('game');
        } else {
            //Not logged in
            //Get facebook login url
            $facebook_data = array(
                'redirect_uri' => 'hxxp://xxxxxxxx.com/facebook_login/',
                'scope' => 'email'
            );
            $data['facebook_login_url'] = $this->facebook->getLoginUrl($facebook_data);
            //Redirect to login form
            $this->load->view('login/login_form', $data);
        }
    }

    public function facebook_login()
    {
        //Always returns 0!! Even after authenticating via facebook!
        $facebook_user_id = $this->facebook->getUser();

        if ($facebook_user_id) {
            try {
                $user_profile = $this->facebook->api('/me');
                print_r($user_profile);
            } catch (FacebookApiException $e) {
                echo $e->getMessage();
            }
        } else {
            echo "Could not log in with Facebook";
        }
    }
}

The stripped down view (login_form.php) is as follows:

<html>
    <head>
        <title>Facebook Connect Test</title>
    </head>
    <body>
        <a href='<? echo $facebook_login_url; ?>'>Login with Facebook</a>
   </body>
</html>

I have a route that redirects hxxp://xxxxxxxx.com/facebook_login to the login/facebook_login method, which is working.

I am running this code on a live development server.

My current flow is as follows:

  1. Load hxxp://xxxxxxxx.com/ (Routes to login controller, which loads login_form view)
  2. Click "Login with Facebook" link
  3. Facebook asks me to login (which I do)
  4. Facebook asks me to give permission to my app (which I do)
  5. Facebook redirects me to the url specified in the redirect_uri parameter, which is identical to the one on the app settings page

And here's where it all goes wrong. The $this->facebook->getUser() method ALWAYS returns 0, even after authentication.

I have been scouring the Facebook developer documentation and everywhere else on the internet I can think of trying to find an answer to this. I have come across many posts similar to this and have tried to apply the solutions suggested, but to no avail.

What am I doing wrong?

Upvotes: 3

Views: 5699

Answers (2)

gbruins
gbruins

Reputation: 426

The getCode() method in base_facebook.php uses the $_REQUEST global to store data. PHP 5.3.0 and greater uses the "request_order" param in php.ini, and by default $_REQUEST does not contain Cookie variables.

Per php.net (http://php.net/manual/en/ini.core.php#ini.request-order):

"This directive describes the order in which PHP registers GET, POST and Cookie variables into the _REQUEST array. Registration is done from left to right, newer values override older values.

If this directive is not set, variables_order is used for $_REQUEST contents.

Note that the default distribution php.ini files does not contain the 'C' for cookies, due to security concerns."

So it looks like your options are to modify the getCode() method like Max Power did above, or update your php.ini and add the "C" value to the request_order setting.

Upvotes: 3

Max Power
Max Power

Reputation: 59

I managed to solve my problem. The questions linked to by Qweick and Stéphane Bruckert had the solution. The problem lies in the getCode() function of the base_facebook.php file.

The getCode() function needs to be modified. The modifications I used are listed below.

Existing non-working code:

protected function getCode() {
    if (isset($_REQUEST['code'])) {
        if ($this->state !== null &&
                isset($_REQUEST['state']) &&
                $this->state === $_REQUEST['state']) {

            // CSRF state has done its job, so clear it
            $this->state = null;
            $this->clearPersistentData('state');
            return $_REQUEST['code'];
        } else {
            self::errorLog('CSRF state token does not match one provided.');
            return false;
        }
    }

    return false;
}

Modified working code:

protected function getCode() {
    $server_info = array_merge($_GET, $_POST, $_COOKIE);

    if (isset($server_info['code'])) {
        if ($this->state !== null &&
                isset($server_info['state']) &&
                $this->state === $server_info['state']) {

            // CSRF state has done its job, so clear it
            $this->state = null;
            $this->clearPersistentData('state');
            return $server_info['code'];
        } else {
            self::errorLog('CSRF state token does not match one provided.');
            return false;
        }
    }

    return false;
}

The getUser() call now returns a valid user Id and the Facebook API calls now return valid data.

Thanks to everyone that helped point me in the right direction!

Upvotes: 2

Related Questions