Facebook OAuth 2.0 via Graph API using PHP and Zend

Coding
By Jasper | October 9, 2010

Recently I added OAuth authentication and some Facebook Graph API calls to Vizited for connecting users to their Facebook accounts, allowing them to share their public bookmarks through Facebook. If you have ever tried implementing OAuth, you’ve probably discovered that it’s quite a challenge the first time. It takes some reading and experimenting before it starts to make sense. Probably because the process exists of some steps (requests) that are a little bit confusing in the beginning.

I’m a huge fan of the Zend Framework which didn’t make the task easier, because there are not a lot of examples available. I love Zend because it makes the PHP-part of my life a lot more fun and, besides this, Vizited is developed in Zend and I wanted to fit the OAuth thingy nicely in. In this article I will explain my approach which will hopefully help others that are struggling with the same idea. The principles in this article can easily be applied without Zend Framework as well.

Unfortunately I don’t have a Zend installation on this blog (yet), so I didn’t create a fully working example. I will show you the essential parts of the code I developed for Vizited and assure you it works. You can test it yourself by creating a free account on Vizited and linking it to Facebook (or Twitter or LinkedIn, which basically use the same technology for authentication). I’m happy to answer any questions you may have, just put them in the comments.

One other thing before you continue reading this: this tutorial is probably not fit for beginners. I’ll assume you have significant knowledge of PHP, Zend and maybe some Javascript, because it will be too much work to explain everything in full detail.

Creating a Facebook Application and updating the Zend application.ini

You should start by creating a Facebook Application. There’s a lot of information available online on how to do this. Although you may not be developing an application that will be displayed “widget-style” on a user’s Facebook account, you’ll still need an application for Graph API calls and the OAuth authentication flow.

After you’ve finished setting up the application in Facebook, you should end up with a client_id and a client_secret (which can be retrieved from the Web Site tab of your application configuration). These we will add to the application.ini in Zend, together with a callback URI, like this:


facebook.client_id = "<your client id>"
facebook.client_secret = "<your client secret>"
facebook.redirect_uri = "http://www.<your domain>.com/social/facebookcb/"

For easy reference, I have the (good?) habit to add application.ini settings to my Zend Registry in my Bootstrap.php, like this:


protected function _initConfig() {

	$aConfig    = $this->getOptions();
	Zend_Registry::set('facebook_client_id', $aConfig['facebook']['client_id']);
	Zend_Registry::set('facebook_client_secret', $aConfig['facebook']['client_secret']);
	Zend_Registry::set('facebook_redirect_uri', $aConfig['facebook']['redirect_uri']);

}

Now you’ll be able to very conveniently reference the attributes like this Zend_Registry::get(‘facebook_client_id’).

Requesting authentication and authorization

Authentication and authorization with Facebook exists of several steps, that are described here. To implement these steps, we have to create a controller in Zend that contains the necessary actions. As you could see in the redirect_uri in the application.ini, our controller is called socialController.

This controller has two main actions: facebookauthAction and facebookcbAction. The first one will start the authentication and authorization flow and handle the first redirect, the second one will handle the callback from Facebook. This is the code for starting the flow:


public function facebookauthAction()
{
	$this->_helper->viewRenderer->setNoRender(true);

	if ($userid = Zend_Auth::getInstance()->getIdentity()->id)
	{
		// Get a Facebook authorization for the publish_stream and offline_access
		$url = 'https://graph.facebook.com/oauth/authorize?client_id=' .
			Zend_Registry::get('facebook_client_id') .
			'&redirect_uri=' .
			Zend_Registry::get('facebook_redirect_uri') .
			'&scope=publish_stream,offline_access';

			// Return URL that should be used for redirection in Javascript
			die ("1|$url");
		}
		else
		{
			// No user logged in
			die ("0|Please login first");
		}
	}
}

Line 8 is the most important one, this is were the URL is compiled. It uses the application.ini settings together with the scope parameter. Make sure you select the right scope for your purpose! The option publish_stream will allow you to post to the stream of a user, offline_access will allow you to do this, even if the user is logged out from Facebook. Details can be found on this page.

Less important, in my code I will only redirect the user if he’s already logged in to the local application (in this case Vizited), else I will ask him to login first. Line 5 takes care of this. Of course this is not required for Facebook.

In line 3 of this code, I turn off the rendering of the view for this action. The reason for this is that I call this action asynchronously from Javascript and I want the result to be returned to the script as well. The returned URL which is created in line 8 should be used to redirect the browser. I do it like this in my Javascript:


// Out of context! jQuery is used to call the facebookauth action
...
$.get('/social/facebookauth/', getFacebookAuthReady);
...

function getFacebookAuthReady(response) {

	var ar_response = response.split("|");
	var responseCode = ar_response[0];
	var responseText = ar_response[1];

	if (responseCode == 1)
 	{
		// Go to URL for Facebook Authorization
 		window.location.href=responseText;
 	}
 	else
 	{
 		// Show error
		...
 	}
}

Basically, it doesn’t matter how you redirect to this URL (you could do this in your view or by using the redirect feature of Zend). I do it this way because I want to display an error to the user if he or she is not logged in. I hope this makes sense.

After you’ve implemented this code, you can test this action by entering www.yourdomain.com/social/facebookauth/ in your browser which should direct you to Facebook, asking you to authorize your application.

Handling the callback

After the redirect to graph.facebook.com/oauth/authorize Facebook will ask the user to login (if not already logged in to Facebook) and to give the requesting application (your application!) permission to his or her account. Next, Facebook will redirect back to the redirect_uri. In our case, this is the facebookcbAction of the socialController. More specifically:


public function facebookcbAction()
{
	$request = $this->getRequest();
	$params = $request->getParams();

	if (isset($params['code']))
	{
		// This is the callback from Facebook, get the code parameter
		$code = $params['code'];

		$url = 'https://graph.facebook.com/oauth/access_token';
		$arpost = array(
			'client_id' => Zend_Registry::get('facebook_client_id'),
			'redirect_uri' => Zend_Registry::get('facebook_redirect_uri'),
			'client_secret' => Zend_Registry::get('facebook_client_secret'),
			'code' => $code);

		// Now request the access_token
		$result = $this->requestFacebookAPI_GET($url,$arpost);

		if ($result === FALSE)
		{
			// Redirect to error page
			...
		}
		else
		{
			parse_str($result, $arresult);

			// Use the access_token to retrieve the user's profile
			$url = 'https://graph.facebook.com/me';
			$arpost = array('access_token' => $arresult['access_token']);

			$result = $this->requestFacebookAPI_GET($url,$arpost);

			if ($result === FALSE)
			{
				// Redirect to error page
				...
			}
			else
			{
				$arprofile = json_decode($result,true);

				$data = array(
					'id' => Zend_Auth::getInstance()->getIdentity()->id,
					'facebook_access_token' => $arresult['access_token'],
					'facebook_name' => $arprofile['name'],
					'facebook_id' =>  $arprofile['id']
				);

				// Save this array in the database as part of the user profile
				// Redirect to success page
				....
			}
		}
	}
}

In line 6, we start with checking if the parameter code is returned by Facebook. This code should be exchanged for an OAuth access token for this user and without it we’re lost. The access token is the key to all the Graph requests we want to perform on behalve of this user. To exchange the code for the access token, we generate a new GET request for which we use the private method requestFacebookAPI_GET with the parameters as defined in line 12.

The homebrew requestFacebookAPI_GET method performs a cURL HTTP request and looks like this:


private function requestFacebookAPI_GET($url, $arpost) {

	//
	// Create a cURL request to Facebook
	//
	$ch = curl_init();

	curl_setopt($ch, CURLOPT_URL, $url . "?" . http_build_query($arpost));
	curl_setopt($ch, CURLOPT_POST, false);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

	$result = curl_exec($ch);
	curl_close($ch);

   	return $result;

}

The result that is returned from this function is parsed after which we have the access token. In line 32 to 34 we use this access token to perform our first Facebook request that actually requires an access token (getting the users profile). The JSON result is then parsed and stored in an array, together with the access token, which you can save to your database for future use.

Publish to the feed/wall of a user

If you decided to obtain publish_stream rights in the authorization step, and the user allowed this, you will be able to publish messages to the user’s feed. I will show you a quick example on how to do this.

First, we’ll need to tweak the requestFacebookAPI_GET to POST instead of GET (trust me, this is not very obvious if you’re trying to figure this out without the right info, until you find this page):


private function requestFacebookAPI_POST($url, $arpost) {

	//
	// Create a cURL request to Facebook
	//
	$ch = curl_init();

	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($arpost));
	curl_setopt($ch, CURLOPT_POST, true);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

	$result = curl_exec($ch);

	curl_close($ch);

   	return $result;

}

There are only tiny differences: CURLOPT_POST is set to true and the CURLOPT_POSTFIELDS are filled instead of the query string, which makes this a POST request. Obviously you could integrate the both in one method, but I decided not to. :-) The overall point is that the Facebook Graph API requires part of its access through POST-requests and part through GET-requests.

Now let’s post something (anything) to the wall:


// Get the access token of the user from the database and
// store it in the $access_token variable
...
$access_token = ...

// Now publish a link to the feed/wall of the user
$fburl = 'https://graph.facebook.com/me/feed';
$arpost = array(
	'link' => 'http://www.jasperrooswinkel.com/',
	'name' => 'Jasper Rooswinkel',
	'caption' => 'Home of Jasper Rooswinkel',
	'description' => 'This is very nice!',
	'access_token' => $access_token
);

$result = $this->requestFacebookAPI_POST($fburl,$arpost);

The result should appear on the wall of the user almost instantly. If not: verify your code, this is tricky and you should test it with simple content. If yes: great success! Btw. it’s a good idea to create a test account on Facebook before testing this on actual users, because all your friends will see the new posts.

That’s it for now. I hope this helps! :-)

11 comments on “Facebook OAuth 2.0 via Graph API using PHP and Zend”

  1. 1 pRtkL xLr8r October 11, 2010 6:01 am

    Umm…so why aren’t you using Zend_Oauth again?

  2. Thanks for your feedback. I encourage reusing existing libraries, but decided not to because:
    1. Zend_Oauth is (still?) conforming to OAuth 1.0 (according to its documentation) which will not work for Facebook, according to online sources.
    2. I doubt the Facebook OAuth/Graph implementation is 100% final and I want to be in control of the complete flow (not depending on external libraries) to be able to act quickly on any future changes.
    3. I’ve also integrated OAuth for Twitter and LinkedIn and it looks like all three, including Facebook, handle it slightly different. To me, this discourages the use of a standard library.

  3. Thanks for this. I tried using Zend_Oauth for Twitter and FB but could only get it to work for Twitter.

    I actually stumbled across this trying to find a way to use Zend_Http_Client to make requests to the graph because it’s causing me all sorts of issues, but your curl method works great

  4. 4 MattiMatti March 28, 2011 1:20 am

    Great Article Jasper!
    I agree with your opinion about Zend_Oauth.

    Your curl method works great with foursquare api calls.

    Thank you.

  5. HI,
    Thanks for the turial…Can you provide download link for above example

  6. I tried it the following way but does not works for me, I have tried file_get_contents but same result. it does not return the access_token, just returns the ‘code’ in the uri, can you please help me find out the issue here?
    $code = $_REQUEST["code"];
    if(empty($code)) {
    $dialog_url = “http://www.facebook.com/dialog/oauth?client_id=”. $appid . “&redirect_uri=” . urlencode($cburl.’addapp..php’) . “&scope=manage_pages”;

    echo(” top.location.href=’” . $dialog_url . “‘”);
    }

    $url = ‘https://graph.facebook.com/oauth/access_token’;
    $arpost = array(
    ‘client_id’ => $appid,
    ‘redirect_uri’ => $cburl.’addapp.php’,
    ‘client_secret’ =>$appsec ,
    ‘code’ => $code);

    // Now request the access_token
    $result = $this->requestFacebookAPI_GET($url,$arpost);
    after this I tried to call the api method to run fql but did not go ahead…
    $response = $facebook->api(array(
    ‘method’ => ‘fql.query’,
    ‘query’ =>$fql,
    ));
    Not sure why the access token is not sent back. please help!

  7. I got most of this working and got stuck at json_decode() function. Is that part of Zend_OAuth?

  8. you forgot to show where the getOptions(); came from because i try to do the same thing in my application it give’s me that getOptions not found

  9. Thanks!!! You saved me couple of hours, fb documentation is pain in the ass.

Your comment