[MODULE] Yiiauth

Example on how to connect with your user system

table for providers:


-- 

-- Structure for table `social`

-- 


DROP TABLE IF EXISTS `social`;

CREATE TABLE IF NOT EXISTS `tbl_social` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `yiiuser` int(11) NOT NULL,

  `provider` varchar(50) NOT NULL,

  `provideruser` varchar(255) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Use gii to create the the model file.

In Your UserIdentity model (usually protected/components unless u have some user module)




public function hybridauth($username)

	{

		$user=User::model()->find("username = '" . $username . "'");

		if ( $user === null ) 

			$this->errorCode = self::ERROR_USERNAME_INVALID;

		else

		{

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

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

			$this->errorCode = self::ERROR_NONE;

		}

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

	}



and for simplicity to make the example the new protected/modules/yiiauth/controllers/DefaultController.php

Its probably bad practise to put all the methods in here but its example demo so…




<?php


class DefaultController extends Controller

{

	public function actionIndex(){

		$this->renderPartial('index');	

	}

	public function actionauthenticatewith( $provider="" ) {   

		require_once( '/hybridauth/Hybrid/Auth.php' );

		$hybridauth_config = $this->hybridAuthConfig();

		$error = false;

		$user_profile = false;

		try{

		// create an instance for Hybridauth with the configuration file path as parameter

			$hybridauth = new Hybrid_Auth( $hybridauth_config );


		// try to authenticate the selected $provider

		if ( isset( $_GET['openid'] ) ){

				$provider = "openid";

				$adapter = $hybridauth->authenticate( $provider,array( "openid_identifier" => $_GET['openid'] ) );

			}else{

				$adapter = $hybridauth->authenticate( $provider );


			}

		// grab the user profile

			$user_profile = $adapter->getUserProfile();

			

		}

		catch( Exception $e ){

			// Display the recived error

			switch( $e->getCode() ){ 

				case 0 : $error = "Unspecified error."; break;

				case 1 : $error = "Hybriauth configuration error."; break;

				case 2 : $error = "Provider not properly configured."; break;

				case 3 : $error = "Unknown or disabled provider."; break;

				case 4 : $error = "Missing provider application credentials."; break;

				case 5 : $error = "Authentification failed. The user has canceled the authentication or the provider refused the connection."; break;

				case 6 : $error = "User profile request failed. Most likely the user is not connected to the provider and he should to authenticate again."; 

					     $adapter->logout(); 

					     break;

				case 7 : $error = "User not connected to the provider."; 

					     $adapter->logout(); 

					     break;

			} 


			// well, basically your should not display this to the end user, just give him a hint and move on..

			$error .= "<br /><br /><b>Original error message:</b> " . $e->getMessage(); 

			$error .= "<hr /><pre>Trace:<br />" . $e->getTraceAsString() . "</pre>";  


		}

		/**$user_profile->identifier; //unique id

		$provider; // the provider name

		$_GET['openid'];//the extra_info

		**/

		

		// workOnUser returns an user object

		$user = $this->workOnUser($provider,$user_profile->identifier); 

		if ( $this->autoLogin($user) ){

			$this->render('profile',

				array(

				'error'=>$error, 

				'provideruser'=>$user_profile,

				'yiiuser'=>$user,

				'provider'=>$provider,	

				) );

			}else{

			$this->render('authenticatewith',array('error'=>$error,'user_profile'=>$user_profile ) );

		}

	} 


	public function workOnUser($provider,$provideruser){

		$social = Social::model()->find("provider='".$provider."' AND provideruser='".$provideruser."'");

		if ( $social ){

			 $user = User::model()->find("id=".$social->yiiuser);

			 return $user;

		}else{

			// we want to create a new user, but since we get no user input the validation rules will cause

			//errors on save to counter this i added 'on'=>'validation' to all my user validation rules

			//example: 	array('username, password', 'required','on'=>'validation'),

			// on normal user registration with user input I use: new User('validation') 

			$user = new User;

			$user->username = $provideruser; 

			

			if ( $user->save() ){ //we get an user id

			//add a social connection between the newly created yii user and the provider user account to avoid double regestrations and enable the same yii user to have many providers associated with it.

				$social = new Social;

				$social->yiiuser = $user->id;

				$social->provider = $provider;

				$social->provideruser = $provideruser;

				if($social->save())

					return $user;

			}

		}

	

	}

	

	public function autoLogin($user) //accepts a user object

	{

	$identity=new UserIdentity($user->username, "");

	$identity->hybridauth($user->username);

	if ( $identity->errorCode == UserIdentity::ERROR_NONE )

		{

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

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

			return true;

		}

		else

		{

			echo $identity->errorCode;

			return false;

		}

	

	}

}?>



then create protected/modules/yiiauth/views/default/profile.php





<?php

	if( isset( $error ) ){

		echo  $error;

	}


	echo $yiiuser->id. "<br/>"; // the yii user id

	echo $yiiuser->username."<br/>"; // the yii user name

	echo $provider ."<br/>"; // provider,twitter,fb etc

	var_dump($provideruser); //the object with all info u mby want to use from the provider

?>



Thanks a lot for posting your progress…I’ve been waiting for something like this for a while…I will test this when I refocus my effort on user/registration…

Thank you, My biggest problem now is A) lack of time, B) I developed my user system for one app and its still pretty tight to that app, meaning If I moved it all to another app I would get a shitload of tiny errors like missing variables etc. So I have to clear all this outh.

Otherwise its… very easy to start up actually.

I noticed I had a problem with authorizing thrue facebook, even though other providers worked perfectly fine.

Now, for some reason facebook didnt like some certificate,so I found a solution that worked for me.

in Hybridauth/Hybrid/Providers/Facebook.php

you can find on row 365 an array, curl opts. Add SSL_VERIFYPEER =>false.

public static $CURL_OPTS = array(

CURLOPT_CONNECTTIMEOUT =&gt; 10,


CURLOPT_RETURNTRANSFER =&gt; true,


CURLOPT_TIMEOUT        =&gt; 60,


CURLOPT_USERAGENT      =&gt; 'facebook-php-3.1',


CURLOPT_SSL_VERIFYPEER =&gt; false // &lt;--- THIS I ADDED

);

I updated the method workOnUser in DefaultController

Now if a Yii user is already logged in, the provider is connected to that user.

This way a user can have multiple provider accounts connected to the same yii-user.




	public function workOnUser($provider,$provideruser){

		$social = Social::model()->find("provider='".$provider."' AND provideruser='".$provideruser."'");

		if ( $social ){

			 $user = User::model()->find("id=".$social->yiiuser);

			 return $user;

		}else{ // no user is connected to that provideruser, 

			$social = new Social; // a new relation will be needed

			$social->provider = $provider; // what provider

			$social->provideruser = $provideruser; // the unique user

			

			// if a yii-user is already logged in add the provideruser to that account

			if ( !Yii::app()->user->isGuest ){

				$social->yiiuser = Yii::app()->user->id;	

				$user = User::model()->findByPk(Yii::app()->user->id);

			}else{

			// we want to create a new user

				$user = new User;

				$user->username = $provideruser; 


				if ( $user->save() ){ //we get an user id

					$social->yiiuser = $user->id;

					}

			}

			if($social->save())

				return $user;

		}

	

	}



And sample code to put on the profile view to show the providers that is connected to that user and allow him to connect more:




<h4> By connecting a social provider to your account you can use it to login<h4>

	<?php $socials = Social::model()->findAll('yiiuser='.$model->id);

		 if($socials){

			echo "<h5>You have currently connected your account with:</h5>";

			foreach($socials as $social){

				echo "* <b>".$social->provider."</b><br/>";

			}

		 }

		$this->widget('LoginWidget');//displays the possible providers

	?>



I there an alternative to put it in vendors folder in yii application instead of the root.

Edit* Yes, just use the new path when you use requiere_once()

(like in authenticatewith action in defaultcontroller…)

But I’m working on fixing some stuff to make these things easier

Does this extension provide a way to post updates to these provider networks? E.g. can it be used to allow Facebook status updates or tweets?

Yes,its not hard at all. I’ve done a method for it that will be included in next release…

But now already you can test with $adapter->setUserStatus( ‘foo’ );

in default controller,after successful auth.

Those who has support for status updates is:




$providers = array('facebook', 'twitter', 'identica', 'linkedin', 'qq', 'sina', 'murmur', 'pixnet','plurk');



Are you missing the providers array in module file in the new update or where did you move it.

I put a check for that in the updateStatus method in Yiiauth component (inside the module).

So if you run $this->updateStatus($provider,$status); it will check if $provider is in that array you showed, if it is it will set the user status to $status. So you do not have to worry about that.

The array for providers you would like to use, is set in the config, how to configure it is shown on the hybridauth info page. i strongly recommend to read that:)

How do you download this?

Where is the download link?

For the extension page (it also contains the download link for the current release):

http://www.yiiframework.com/extension/yiiauth/

The second line is error because of the first error, and I dont know why that line would give you an error if the login works, that same Yii::app()->controller->module->config is ran on login to… hmm

Yes…that’s why i think the problem shouldn’t be there…

So weird…

Is that works on you?

Hm, I will test it. I misread your first post, didnt see what method you were using.

I dont have time for it right now, but I will probably do it tomorrow:p

Thanks so much! That’s a great great module! :D

Ah, It didnt work for me either first, I found I had typo.

At approximately line 145




public function updateStatus($provider,$status){

	$this->newAuth();

//Change to

public function updateStatus($provider,$status){

$hybridauth = $this->newAuth();



Then it should work,atleast did so for me:)

I found that typo and had corrected it yesterday…So this is not the problem…

Still give me "Trying to get property of non-object"…

Don’t know why the same method is just not working here…!!!

-.-

I think I’ve followed every instruction verbatim, but I’m afraid I’m getting this:

Error 404

The system is unable to find the requested action "authenticatewith?openid=http:".