Login using Facebook Connect with Zend Framework

It took me a little while to figure out how to add a Login with Facebook mechanism to a website written with Zend Framework. It was a quite frustrating process that included a lot of Google searches and scavenging of data from a several sources.

Which led me to writing this post, hopefully if you got here through Google you could figure out all that you need from the following code, and if not, I apologize in advance.

In order to allow users to login through Facebook’s users system, Facebook has to make sure that your users allow you that privilege. So first you need to create a Facebook application (You don’t actually write an application, just create a new one in it’s database) which will enable Facebook users to allow you to let them log in.

Get into https://developers.facebook.com/apps if it’s the first time you setup an app you’ll have to go through some of Facebook’s settings forms. When you reach the main applications page click the + Create New App button. Define your App Display name, agree to the terms  and continue.

After the app is created you need to select how your app integrates with Facebook, choose Website – I want to allow people to log in to my website using Facebook and set your website’s URL. Facebook will allow login interaction through this app only to the specified URL. Take note to your App ID you will need it later.

We are done with all the preparations, now it’s time for some coding. In order for Facebook to allow you to access it’s user data it needs to authenticate you (Check if you are who you say you are) and then authorize you (Check if you have permission to do what you’re asking). The easiest way to do it is to let Facebook handle that process using the Facebook Javascript SDK. After we accomplish a valid login using the JS SDK we take it’s access token and pass it to our Zend Framework Login controller which will complete the login using a custom authentication adapter.

So first we need simple Login button, when that button is clicked a Facebook window will pop up asking the user if he allows your website to access it’s details (name,email,etc). If the user clicks allow we can proceed. So here’s the button code, the only important thing here is it’s id which is later used.

<button id="fbc-login-button">Login with Facebook</button>

Next we need to load the Facebook Javascript SDK, initialize it and give our button some functionality.
fbAsyncInit – Loads the SDK asynchronously, which means simultaneously to other elements of the web page.
FB.init – Settings, our info (app id) and what kind of capabilities we want from the SDK.

<script type="text/javascript">
window.fbAsyncInit = function() {
	FB.init({
		appId      : 'YOUR_APP_ID',
		status     : true, // check login status
		cookie     : true, // enable cookies to allow the server to access the session
		oauth      : true, // enable OAuth 2.0
		xfbml      : true  // parse XFBML
	});

	button = document.getElementById('fbc-login-button');
	button.onclick = function() {
		FB.getLoginStatus(function(response) {
			if (response.authResponse) {
				this.location = 'https://www.aviran.org/login/facebook/token/' +  response.authResponse.accessToken;
			}
			else {
				FB.login(function(response) {
					if (response.authResponse) {
						this.location = 'https://www.aviran.org/login/facebook/token/' +  response.authResponse.accessToken;
					}}, {scope: 'email'});
			}
		});

	};
};

(function(d){
     var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
     js = d.createElement('script'); js.id = id; js.async = true;
     js.src = "//connect.facebook.net/en_US/all.js";
     d.getElementsByTagName('head')[0].appendChild(js);
   }(document));
</script>

Lets look in detail on what’s going on after the initialization. Our button gets the following function as it’s onClick function. First it checks if the user is logged in to Facebook using FB.getLoginStatus(), if it is logged in resoponse.authResponse would not be null and would contain the access token, in that case the function would redirect to the Facebook Action of our Login Controller and pass it a GET variable called token containing the access token given by Facebook authorization process.
If the user is not logged in (the else clause), a call to FB.login would allow the user to log in and give permission to access its details using a pop up window. A successful login and user consent to allow access to it’s details will result in response.authResponse to not be null and containing our desired access token. All there is left to do is to pass it to our Login controller. Note that we set the attribute scope to ’email’ which means we are asking for user details only.

function() {
	FB.getLoginStatus(function(response) {
		if (response.authResponse) {
			this.location = '/login/facebook/token/' +  response.authResponse.accessToken;
		}
		else {
			FB.login(function(response) {
				if (response.authResponse) {
					this.location = '/login/facebook/token/' +  response.authResponse.accessToken;
				}
			}, {scope: 'email'});
	}
}

Everything is ready on the client side, we have a button that when pushed will log in the user, get the access token,  pass it to our PHP script which will be able to access our Facebook user data using the access token on the server side. Lets do some PHP, we need a controller called Login with an action called Facebook that will handle logins from Facebook using a custom authentication adapter.

Lets look at the custom adapter, it gets the access token and downloads a webpage from Facebook, in it’s URL we supply the access token, Facebook in return will send the user details which will be saved in the variable $details. From here it’s up to you, In this example I call a fictional function that checks if the user logged in previously, if so it gets it’s details and if not it registers the user in the database using another fictional function. In your project you will have to fill those blanks.

class Zend_Auth_Adapter_Facebook implements Zend_Auth_Adapter_Interface  {
	private $token = null;
	private $user = null;

	public function __construct($token) {
		$this->token = $token;
	}

	public function getUser() {
		return $this->user;
	}

	public function authenticate()	{
		if($this->token == null) {
			return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,
						    false, array('Token was not set'));
		}

		$graph_url = "https://graph.facebook.com/me?access_token=" . $this->token;
		$details = json_decode(file_get_contents($graph_url));
		$user = lookUpUserInDB($details->email); // NOT AN ACTUALL FUNCTION
		if($user ==  false) { // first time login, register user
			registerUser($user) // NOT AN ACTUAL FUNCTION
		}
		$this->user = $user;
		return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS,$user);
	}
}

The last piece of the puzzle is our Login controller which is called by our JS above and uses our Facebook authentication adapter. This is the Facebook action in the Login controller which handles the login from Facebook.
You will want to add some redirections, and maybe save a session at some point but this is the basic idea.

public function facebookAction() {
	$token = $this->getRequest()->getParam('token',false);
	if($token == false) {
		return false; // redirect instead 
	}

	$auth = Zend_Auth::getInstance();
	$adapter = new Zend_Auth_Adapter_Facebook($token);
	$result = $auth->authenticate($adapter);
	if($result->isValid()) {
		$user = $adapter->getUser();
		$auth->getStorage()->write($user);
		return true; // redirect instead 
	}
	return false; // redirect instead 
}

The End.