Yii OpenID with LightOpenID

I struggled to get a simple Google log-in to my application working recently and so now that I finally got it working, I decided I would share it in a blog post.

Right up front here is the large block of code in my SiteController (found in /protected/controllers/) that executes the openID with Google.

public function actionOpenid(){ // require openid require dirname(FILE).DIRECTORY_SEPARATOR.'../extensions/openid.php'; try { $openid = new LightOpenID(Yii::app()->request->serverName); if(!$openid->mode) { if(isset($_GET['login'])) { $openid->identity = 'https://www.google.com/accounts/o8/id'; $openid->required = array( 'namePerson/first', 'namePerson/last', 'contact/email', ); header('Location: ' . $openid->authUrl()); } } elseif($openid->mode == 'cancel') { // user cancelled login $this->redirect(array('/site/login')); } else { if($openid->validate()){ $attributes = $openid->getAttributes(); // check if they have been here before $user_check = User::model()->findByAttributes(array('openid_identity'=>$openid->identity)); if($user_check=null){ // they havn't logged in before, add them to the system $new_user = new User(); $new_user->fname = $attributes['namePerson/first']; $new_user->lname = $attributes['namePerson/last']; $new_user->email = $attributes['contact/email']; $new_user->openid_identity = $openid->identity; if($new_user->validate()){ $new_user->save(); } } // initialise them $identity=new UserIdentity($openid->identity,''); $identity->authenticate(); if($identity->errorCode=UserIdentity::ERROR_NONE){ $duration = 36002430; // 30 days Yii::app()->user->login($identity,$duration); } $this->redirect(Yii::app()->homeUrl); } else { // user not logged in $this->redirect(array('/site/login')); } } } catch(ErrorException $e) { echo $e->getMessage(); }}

Here is the modifications I have made to my UserIdentity component (found in /protected/components/):

public function authenticate(){ // search either by email or openid identity $users = User::model()->find('email=:id OR openid_identity=:id', array( ':id'=>$this->username )); if($users===null) { $this->errorCode = self::ERROR_USERNAME_INVALID; } else if(strlen($users->openid_identity) == 0 && !$users->validatePassword($this->password)) { $this->errorCode = self::ERROR_PASSWORD_INVALID; } else { $this->errorCode = self::ERROR_NONE; $this->_id = $users->id; $this->setState('fname', $users->fname); $this->setState('lname', $users->lname); $this->setState('email', $users->email); } return !$this->errorCode;}

Now I will go over the assumptions this code has one-by-one.

LightOpenID

You will need to download LightOpenID from the Google code page

Assumptions

When the login fails, user cancels, etc, you want to send them back to the '/site/login' route, if this is not the controller function you want them to bounce back to, change it to be appropriate.I also assumer you have a User model that represents your user database.  This user also has a fname (first name), lname (last name), email and openid_identity. This code also assumes that you to log in your user, the link (for the login with google button) goes to your /sitecontroller/openid route (for example my url on my link button looks like: "index.php?r=site/openid".

Explaining the logic

Once the user hits this route (function in the controller), it passes the user through to Google requesting the data. Google then sends the user back to our Openid route, if it failed, we redirect the user back to the login page. If it succeeds, we check if the users openid identity is already in our database, if it is not, we create them as a user. We then sign this user in with our UserIdentity model and once signed in we send them back to our home url.

Further Reading

https://developers.google.com/accounts/docs/OpenID

Need Help?

Let me know in a comment below.