Getting Started with Facebook Connect login - PHP

Introduction.

I considered using Facebook Connect PHP API to provide login capabilities for one of my pages recently, but as is often the case I was disappointed with both the documentation and the example provided.

The example is confusing because it does not address any particular use case, and it features user information and the profile picture of the owner of the associated Facebook App. The latter has little or nothing to to with the context.

It is also confusing because if you press its 'Logout' link, nothing much happens, and it is not clear how to get back to the login state so that, for instance, you can log in as someone else.

What I wanted was just an example that would allow you to log in, log out, and collect some minimal information about the user along the way.

I should be clear that I am commenting on the php-sdk implementation. For one thing, the JavaScript implementation and tutorials about it are just impenetrably awful. For another I use a LAMP server, and I live in Africa. My web server in New Jersey can communicate with a Facebook server to do the login stuff vastly more quickly than Ajax calls from my browser could. So it seems to me that this system is best distributed between the server and the browser. A further advantage is that if your page is jQuery oriented, the server/client division avoids conflicting Javascript coding styles in your page.

To get started, you'll need to download the Facebook PHP-SDK. Then you'll need to create a Facebook App - or at least a trace of one. Log into Facebook then navigate to facebook.com/developers. If you're not already registered you may have to sign up, but sooner or later you'll arrive at a page that has a 'Create New App' link. Only minimal information is required for our example:


The 'Display Name' is what will be shown to the user during login to ask if it is OK to give certain information to your web application. The 'Site URL' is the URL you'll type into the browser to invoke your page. Save the settings, and you will be given an 'App ID' and an 'App Secret'. You'll need to paste these into the example code.

Unzip the PHP-SDK stuff, and put files example.php, facebook.php, and base_facebook.php into the directory corresponding to the site URL you entered in the Facebook app settings. Here's the example, with some extra comments added and CSS and other irelevancies removed. I apologize for the defective highlighting, and will try to sort it out. This section is really just sniping, and if you don't need that, just skip down to the alternative example that follows.


<?php
require 'facebook.php';

// Create our Application instance (replace this with your appId and secret).
$facebook = new Facebook(array(
  'appId'  => '123456789012345',
  'secret' => 'abcdef1234567890abcdef1234567890',
));

// Get User ID
$user = $facebook->getUser();

// We may or may not have this data based on whether the user is logged in.
/*****************************************************************************
This should probably say 'whether a user is logged in, from this browser' - i.e. session cookies?  */
//
// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid. An access
// token is invalid if the user logged out of Facebook.

if ($user) {
  try {
    // Proceed knowing you have a logged in user who's authenticated.
    $user_profile = $facebook->api('/me');
  } catch (FacebookApiException $e) {
    error_log($e);
    $user = null;
  }
}
/*****************************************************************************
The bit above works fine - should say 'Proceed presuming'. I think it is fair to say
that the logout bit below is broken. */

// Login or logout url will be needed depending on current user state.
if ($user) {
  $logoutUrl = $facebook->getLogoutUrl();
} else {
  $loginUrl = $facebook->getLoginUrl();
}

/*****************************************************************************
The following bit is a red herring designed to confuse the user - we already have
the information we need. */
// This call will always work since we are fetching public data.
$naitik = $facebook->api('/naitik');

?>
<html>
  <head>
    <title>php-sdk</title>
    </style>
  </head>
  <body>
    <h1>php-sdk</h1>

    <?php if ($user): ?>
      <a href="<?php echo $logoutUrl; ?>">Logout</a>
    <?php else: ?>
      <div>
        Login using OAuth 2.0 handled by the PHP SDK:
        <a href="<?php echo $loginUrl; ?>">Login with Facebook</a>
      </div>
    <?php endif ?>

/*****************************************************************************
For the purposes of a logon demo, this is just fluff. */
    <h3>PHP Session</h3>
    <pre><?php print_r($_SESSION); ?></pre>

/*****************************************************************************
For a login demo, this should display the logged in user name, not 'You', but
the picture is the logged-on user. */
    <?php if ($user): ?>
      <h3>You</h3>
      <img src="https://graph.facebook.com/<?php echo $user; ?>/picture">

/*****************************************************************************
This is almost fluff, but it's useful to know what user info is available
to you in a bare login setup. */
      <h3>Your User Object (/me)</h3>
      <pre><?php print_r($user_profile); ?></pre>
    <?php else: ?>
      <strong><em>You are not Connected.</em></strong>
    <?php endif ?>

/*****************************************************************************
This is just downright confusing, and should not be here
    <h3>Public profile of Naitik</h3>
    <img src="https://graph.facebook.com/naitik/picture">
    <?php echo $naitik['name']; ?>
  </body>
</html>

Let's chop this down to something more manageable that works in a clearly sequenced server then client way. We'll connect to Facebook from the server to get info about any logged-on user, and copy that into hidden input variables so we can use it in Javascript in the browser.

The code presented also deals with the logout issue in a simple and easily understood way. The observed problem with the original example is that the logout link does not have any noticeable effect. This is because there is no provision for destroying the related cookie and PHP session.

These operations need to be performed server side in the PHP code. The logout URL is modified so that it has a vestigial query string. Then in the PHP code, the first thing we do is to check for a $_GET variable indicating that the Facebook logout has taken place. If that is present, we zap the session information, then redirect back to the page without the query string.


<?php
require 'facebook.php';

// Create our Application instance (replace this with your appId and secret).
$facebook = new Facebook(array(
  'appId'  => '421371531230227',
  'secret' => 'ef01aa7360fe4faba65fd14f32088ab3',
));

if( isset( $_GET['logged_out'] ) ) {
    setcookie( "PHPSESSID", "", (time()-3600), '/' );
    session_destroy();
    header("Location: http://localhost/php/feed.php");
    exit();
}

// Get User ID
$user = $facebook->getUser();

// We may or may not have this data based on whether the user is logged in.
//
// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid. An access
// token is invalid if the user logged out of Facebook.
$user_profile = null;
if ($user) {
  try {
    // Proceed presuming you have a logged in user who's authenticated.
    $user_profile = $facebook->api('/me');
  } catch (FacebookApiException $e) {
    error_log($e);
    $user = null;
  }
}
$logged = false;
$authUrl = "";

// Login or logout url will be needed depending on current user state.
if ($user) {
  echo $user_profile["name"]." Logged in<br>\n";
  $logged = true;
  $params = array( 'next' => 'http://localhost/php/feed.php?logged_out' );
  $authUrl = $facebook->getLogoutUrl($params);
} else {
  echo "Not logged in<br>\n";
	$authUrl = $facebook->getLoginUrl(array(
		'scope'	=> 'user_location'
		));
}

// For now the HTML has just either a Login or a Logout link. If logged in,
// the logged-in user profile info is displayed.
if ($logged)
{
   echo "<a href='$authUrl'>Logout</a><br>\n";
   echo "<pre>";
   print_r($user_profile);
   echo "</pre>\n";
}
else
   echo "<a href='$authUrl'>Login</a><br>\n";
?>
To progress from this to a clear distinction between server side PHP and client side Javascript can go as follows. The main page starts with some hidden input elements that are populated with the FB authorization state and user data by PHP as the page is served. The jQuery initialization code for page load completed can then use these values in any way you please.

...
// Code before here as previous example

// Login or logout url will be needed depending on current user state.
if ($user) {
  $logged = true;
  $params = array( 'next' => 'http://localhost/php/feed.php?logged_out' );
  $authUrl = $facebook->getLogoutUrl($params);
} else {
	$authUrl = $facebook->getLoginUrl(array(
		'scope'	=> 'user_location'
		));
}
?>

<html>
<head>
<title>BEV Facebook Connect login example.</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"/></script>
<script>
var loginStatus = "out";
var fburl;
$(function() {
   var loginStatus = $('#fb_authstatus').val();
   var fburl = $('#fb_authurl').val();
   $('#authbutton').click(function() { document.location = fburl; });
   if (loginStatus == 'in')
   {
      $('#authbutton').html("Logout");
      var info = $('#fb_name').val() + " - " + $('#fb_id').val();
      $('#userinfo').html(info);

   }
});
</script>
</head>
<body>
<!-- This is the only point at which PHP must intrude into an existing page.
     Fill in whatever you need from the FB profile info here. -->
<input type="hidden" id="fb_authstatus" value="<?php echo $logged? 'in': 'out' ?>" />
<input type="hidden" id="fb_id" value="<?php echo $user; ?>" />
<input type="hidden" id="fb_name" value="<?php echo $user_profile['name']; ?>" />
<input type="hidden" id="fb_authurl" value="<?php echo $authUrl; ?>" />
<!-- PHP bows out. -->

<button id="authbutton">Login</button><p/>
User info: <span id="userinfo">(Not logged in)</span>
</body>
</html>
I'm thinking I might put the PHP parts of this into a small iframe at the top of my page. The iframe could then call a function on the main page to hand over its data. Then the actual page could be decoupled from the authentication code. I'll expand this article to cover that approach when I've tried it.