Using Facebook authentication with Yii (PHP)

Note: There many ways to implement this, but this seemed to make the most sense to me at the time.

A login can either come from someone clicking a login link and being sent to a login page or we can force the login and not allow guests.  The login/logout button is easy enough, just modify the distributed site controller’s login and logout “action” methods.  In order to force the login, the best wat to do this is to implement a behavior.  Please see Larry Ullman’s blog for more information on that.

Then we get to the IdentityFactory.  Yii has a nice configuration system in place for its components, so I did some Identity components extending the standard Yii UserIdentity that was included with the Yii distribution. I have the login entry point call on the identity factory which checks a param in the config and returns an instance of the appropriate object.

These objects are where all authentication type specifics happen.  Lets take a look at a simple flow diagram:

Facebook auth flow

So the IdentityFactory chooses which identity we’re using based on the config.  It will send the request over to the FacebookIdentity, which gets all the info needed for the UserIdentity class to query the database, update information there, and then set the user session.

The more interesting part is the connection between FacebookIdentity and the Facebook SDK.  For this I made use of a Yii extension.  I used yii-facebook-opengraph, which, while not the most mature of facebook connect extensions, is the most actively developed and the closest to functional.  (Last year Facebook made a huge change to their SDK which is not at all backwards compatible so they broke most extensions that exist and most developers did not make updates to their extensions.)  This extension only needed one method added to help deal with the Facebook problem with access tokens.

// had to add this function to deal with php's poor handling of expired access tokens
public function setAccessToken($access_token){
return $this->_getFacebook()->setAccessToken($access_token);


Problems with the Facebook PHP SDK

There were a few reasons I wanted to add Facebook authentication to the new iteration of Quizmo.
  • I wanted a second form of authentication to help in abstracting authentication.
  • I needed a second form of authentication because the intention is to open source this project, and I need to provide something other than iSites authentication if I want people to use it.
  • The process is very similar to Harvard’s PIN authentication, which I have done before, but probably won’t be necessary for Quizmo as it will be available through iSites.
  • “Everyone” uses Facebook so it’s probably something worth knowing.
The first minor problem was there is no proxy support.  The Facebook SDK takes you to a Facebook login page to authenticate, but before it sends you there, it checks if you’re already logged in.  This is done through a curl request.  The problem comes if you’re working in a development environment which doesn’t have a direct outlet to the internets.  The curl options are set as

public static $CURL_OPTS = array(
CURLOPT_USERAGENT => 'facebook-php-3.1',

So this is stored as a static variable in the abstract BaseFacebook class – which is absurd because that means it can’t be overridden.  As such the following line has to be added to the BaseFacebook class:

CURLOPT_PROXY => "my.proxy.address:myport",

The larger problem with the Facebook PHP SDK is its inability to handle token errors.  The tokens are set server side when someone authenticates the first time.  These tokens then expire and the SDK is unable to handle errors that occur because of this.  The weirdest thing about this error is that the developers have a solution, they just expect people using their SDK to implement it on their end.  Regardless, their code did not work for me as advertised and had to be tweaked as such:
private function checkAccessToken(){

$app_id = ;
$app_secret = ;
$my_url = ;

// known valid access token stored in a database
$access_token = ->getAccessToken();

$code = @$_REQUEST["code"];

// If we get a code, it means that we have re-authed the user
//and can get a valid access_token.
if (isset($code)) {
. $app_id . "&redirect_uri=" . urlencode($my_url)
. "&client_secret=" . $app_secret
. "&code=" . $code . "&display=popup";

// Now all file stream functions can use this context.
$response = $this->curl_get_file_contents($token_url);
$params = null;
parse_str($response, $params);
$access_token = @$params['access_token'];

// Attempt to query the graph:
$graph_url = ""
. "access_token=" . $access_token;
$response = $this->curl_get_file_contents($graph_url);
$decoded_response = json_decode($response);

//Check for errors
if (@$decoded_response->error) {
// check to see if this is an oAuth error:
if ($decoded_response->error->type== "OAuthException") {
// Retrieving a valid access token.
$dialog_url= ""
. "client_id=" . $app_id
. "&redirect_uri=" . urlencode($my_url);
error_log("top.location.href='" . $dialog_url);
else {
error_log("other error has happened");
else {
// success
//error_log("success" . $decoded_response->name);

return $access_token;