Implementing Facebook Login

Hi guys,

I’m trying to implement a Facebook login on my test application. There are extensions out there but I just don’t feel comfortable using it. I’m just thinking to implement a simple and basic authentication that does only what you require. I’ve dug around in Yii forum to see how others have implemented it and also learned more on Yii’s basic authentication tutorial especially on the authentication flow.

Here’s my code so far.

login.php




<p>

	<?php echo CHtml::link("Login with Facebook", array('site/facebook')); ?>

</p>



SiteController.php




<?php

public function actionFacebook()

{

	$model=new LoginForm;

	if($model->facebook())

		$this->redirect(Yii::app()->user->returnUrl);

}

?>



models/LoginForm.php




<?php

public function facebook()

{

	Yii::import('application.vendors.facebook.Facebook');

	$facebook = new Facebook(array(

		'appId'  => Yii::app()->params['facebookApi'],

		'secret' => Yii::app()->params['facebookSecretCode'],

		'cookie' => true,

	));

	$loginUrl = $facebook->getLoginUrl(array());

	$facebookUid = $facebook->getUser();

	if(!empty($facebookUid))

	{

		$facebookProfile = $facebook->api('/me');

		if(!empty($facebookProfile))

		{

			$user = User::model()->findByAttributes(array('oauth_provider' => 'facebook', 'oauth_uid' => $facebookProfile['id']));

			if($user === null)

			{

				$user = new User;

				$user->oauth_provider = 'facebook';

				$user->oauth_uid = $facebookProfile['id'];

				$user->group_id = 2;

				$user->username = $facebookProfile['username'];

				$user->name = $facebookProfile['name'];

				$user->status = 1;

				$user->ip = $_SERVER['REMOTE_ADDR'];

				$user->created = time();

				$user->modified = time();

				$user->save(false);

			}

			$identity = new UserIdentity($user->id, $user->oauth_uid, $user->oauth_provider);

			$identity->authenticate(true);

			switch($identity->errorCode)

			{

				case UserIdentity::ERROR_NONE:

					$duration = 3600*24*30; // 30 days

					Yii::app()->user->login($identity, $duration);

					return true;

					break;

				case UserIdentity::ERROR_STATUS_NOTACTIV:

					exit('account not active'); //Not very sophisticated

					return false;

					break;

				case UserIdentity::ERROR_STATUS_BAN:

					exit('account banned'); //Not very sophisticated

					return false;

					break;

			}

		}

	}

	else

	{

		echo "<script type='text/javascript'>top.location.href = '$loginUrl';</script>"; //Redirect user to Facebook login page

		exit;

	}

}

?>



components/UserIdentity.php




<?php

class UserIdentity extends CUserIdentity

{

	private $_id;

	public $oauth_uid;

	public $oauth_provider;


	const ERROR_STATUS_NOTACTIV=4;

	const ERROR_STATUS_BAN=5;


	public function authenticate($facebook = false)

	{

		if($facebook)

		{

			Yii::import('application.vendors.facebook.Facebook');

			$facebook = new Facebook(array(

				'appId'  => Yii::app()->params['facebookApi'],

				'secret' => Yii::app()->params['facebookSecretCode'],

				'cookie' => true,

			));

			$facebookUid = $facebook->getUser();

			$user = User::model()->findByAttributes(array('oauth_provider' => 'facebook', 'oauth_uid' => $facebookUid));


			if($user === null)

			{

				$this->errorCode=self::ERROR_USERNAME_INVALID;

			}

			else if($user->status==0)

				$this->errorCode=self::ERROR_STATUS_NOTACTIV;

			else if($user->status==-1)

				$this->errorCode=self::ERROR_STATUS_BAN;

			else

			{

				$this->_id = $user->id;

				$this->oauth_uid = $user->oauth_uid;

				$this->oauth_provider = $user->oauth_provider;

				$this->username = $user->username;

				$this->errorCode=self::ERROR_NONE;

			}

		}

		else {

			$record = User::model()->findByAttributes(array('username' => $this->username));

			if($record === null)

				$this->errorCode=self::ERROR_USERNAME_INVALID;

			else if(!$record->validatePassword($this->password))

				$this->errorCode=self::ERROR_PASSWORD_INVALID;

			else

			{

				$this->_id = $record->id;

				$this->username = $record->username;

				$this->errorCode=self::ERROR_NONE;

			}

		}

		return $this->errorCode==self::ERROR_NONE;

	}


	public function getId()

	{

		return $this->_id;

	}


}

?>



I think I could improve on the error handling especially models/LoginForm.php. Unlike form based login where we can use


$this->addError('attribute','error message')

, Facebook login is form-less on the application itself. So I need some other way to show the error, perhaps a flash message. What do you guys think?

Also, should saving of new users be done in models/LoginForm.php or components/UserIdentity.php? What do you guys think?

I’m still learning Yii as a matter of fact so please review it and let me know how I can improve it.