OpenId
A very popular login method now a days is to allow users to login through their google, openid etc accounts.
One popular protocol being used is the OpenId.
According to wikipedia :
OpenID is an open standard that describes how users can be authenticated in a decentralized manner, eliminating the need for services to provide their own ad hoc systems and allowing users to consolidate their digital identities.
So in simple terms, instead of asking a user for his login credentials, a site asks an openid provider to authenticate a user for the site. This is because the OpenId provider is trusted to already have verified all details of the user.
Accepting OpenId logins on a website has many benefits like :
1. Quick form-less registration for users. Makes user experience better. Hence increased registration/conversion rates.
2. Lesser validation of user information like email verification etc.
3. Quicker logins. Users are mostly logged in to their favorite openid providers, so the login takes place just by making a click!
Over here we shall see how to implement the "Login with Google" feature to a website using Google's openid provider.
Implementing google login with openid is quite easy and takes only a few minutes. To read the technical details in depth, check the documentation. The documentation is available at https://developers.google.com/accounts/docs/OpenID , but might be confusing for beginners, before an example.
LightOpenId
So the first thing to do is to get an OpenID library. Basically this library speaks the OpenId protocol to exchange information with an OpenId provider (Google over here). We shall use LighOpenId. Its a small and simple class that can be used to write OpenId clients (applications that use openid).
It can be downloaded here :: http://gitorious.org/lightopenid/lightopenid/archive-tarball/master.
For a list of libraries for other languages check here
google_login.php
lightopenid has an example for google login. Its very short and simple.
<?php # Logging in with Google accounts requires setting special identity, so this example shows how to do it. require 'openid.php'; try { # Change 'localhost' to your domain name. $openid = new LightOpenID('localhost'); if(!$openid->mode) { if(isset($_GET['login'])) { $openid->identity = 'https://www.google.com/accounts/o8/id'; header('Location: ' . $openid->authUrl()); } ?> <form action="?login" method="post"> <button>Login with Google</button> </form> <?php } elseif($openid->mode == 'cancel') { echo 'User has canceled authentication!'; } else { echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.'; } } catch(ErrorException $e) { echo $e->getMessage(); }
The above code will present a button for "login with google". If the user clicks it, he/she is taken to google.com and asked to login and then allow the source site to share information. If the user allows, then the page will redirect back to google_login.php (the original site) with the details in get parameters. Those details can now be used to register the user on the system and log him in.
Get Profile Information
Here is the same code, with modifications to request profile information of the user.
//Logging in with Google accounts requires setting special identity, so this example shows how to do it. require 'openid.php'; try { # Change 'localhost' to your domain name. $openid = new LightOpenID($_SERVER['HTTP_HOST']); //Not already logged in if(!$openid->mode) { //The google openid url $openid->identity = 'https://www.google.com/accounts/o8/id'; //Get additional google account information about the user , name , email , country $openid->required = array('contact/email' , 'namePerson/first' , 'namePerson/last' , 'pref/language' , 'contact/country/home'); //start discovery header('Location: ' . $openid->authUrl()); } else if($openid->mode == 'cancel') { echo 'User has canceled authentication!'; //redirect back to login page ?? } //Echo login information by default else { if($openid->validate()) { //User logged in $d = $openid->getAttributes(); $first_name = $d['namePerson/first']; $last_name = $d['namePerson/last']; $email = $d['contact/email']; $language_code = $d['pref/language']; $country_code = $d['contact/country/home']; $data = array( 'first_name' => $first_name , 'last_name' => $last_name , 'email' => $email , ); //now signup/login the user. process_signup_login($data); } else { //user is not logged in } } } catch(ErrorException $e) { echo $e->getMessage(); }
Signup and Login
In the above example we get the user's email , first name and last name. Other details are also available. The process_google_data function shall signup the user and log him in. First we check if the email already exists in 'our' database. If it does, then we just have to login the user. If it does not, then we save it in database (sign up) and then login the user.
Flow :
1. Check if email already registered. If yes, jump to step 3.
2. Signup the user. Save the email in database
3. Login the email.
function process_signup_login($data) { $email = $data['email']; $username = $data['username']; $source = $data['source']; $result = $this->db->get_where('users' , array('email' => $email)); //if the user already exists , then log him in rightaway if($result->num_rows() > 0) { //already registered , just login him $row = $result->row_array(); $this->do_login($row); } //new user, first sign him up, then log him in else { //register him , and login $toi = array( 'email' => $email , 'username' => $username , 'password' => md5($this->new_password()) , 'source' => $source , ); $this->db->insert('users' , $toi); $result = $this->db->get_where('users' , array('email' => $email)); if($result->num_rows() > 0) { $row = $result->row_array(); $this->do_login($row); } } //redirect to somewhere redirect(site_url()); } /** Do login taking a row of resultset */ function do_login($row) { session_set('uid' , $row['id']); session_set('email' , $row['email']); session_set('logged_in' , true); return true; }
Once the do_login function sets the session data to login the user, the process is complete.
Google provides the email address and other personal details. It does not have a username. So if your site requires a user to have a valid and unique username, then it has to be managed in someway. The simplest method would be to generate a user based on the first and last name of the user. For example
$username = $first_name . '_' . $last_name;
But again this has to be checked to be unique. If duplicates exist, then may be a number can be added to it.
An alternative approach would be to make the user fill a username after the above step of login is complete, but this would surely be lengthy.
Theory
It is worthwhile to know brief details of how this openid process works. There are plenty of diagrams on wikipedia and the openid provider docs pages, but they do not explain well. Here is a bit of "technical" explanation of how the entire openid authentication process works.
1. User clicks "Login with Google" on a website A. 2. Now website A calls the "Google openid endpoint url" which it already knows, and that is "https://www.google.com/accounts/o8/id". This url replies with an xml document that contains some openid related details including the openind URI. The URI is found to be "https://www.google.com/accounts/o8/ud". 3. Now website A prepares a complex url using the openid URI and its own details and redirects the user to the openid URI. Here is a sample url. https://www.google.com/accounts/o8/ud?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.return_to=http://localhost/openid.php?login&openid.realm=http://localhost&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select Note the openid.return_to parameter in the above url. Google is told to return to that domain after the authentication process completes. The user is redirected to the above url which is inside google.com. At google.com the user will be asked for login details and permission to reveal date to website A. If the user authorises then google.com will again redirect the user back to website A with a url containing some data in GET parameters. Here is a sample url http://localhost/openid.php?login&openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://www.google.com/accounts/o8/ud&openid.response_nonce=2013-03-12T04:32:41ZJf7UtnXq2PwUzw&openid.return_to=http://localhost/openid.php?login&openid.assoc_handle=AMlYA9V0GZ69Jj_uIla0dkrHJJuvafxaYAqQFV9pBtXkYAcnnz9PggEx&openid.signed=op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle&openid.sig=lwIF9fNpWQI5Kubhl3ISxcnwsfw=&openid.identity=https://www.google.com/accounts/o8/id?id=AItOawkVw2jNyV9c8CB2ziGZI1vxp4xE910lpe8&openid.claimed_id=https://www.google.com/accounts/o8/id?id=AItOawkVw2jNyV9c8CB2ziGZI1vxp4xE910lpe8 4. Now website A has some details about the authentication in the GET parameters. Most importantly the claimed_id. Now website A will again contain google.com and ask if this claimed_id is a valid one or not. Now google.com will say that "Yes" this is a valid id that was generated just a while ago by a certain user whose details are "these". The "these" will contain the details which website A had earlier requested.
Now website A has verified details of the current user. It can now proceed with the rest of the login/signup process. If the user is already registered on website A then the website has to login the user or if the user is not already registered then the website A has to first register and then login.
Apart from Google, Yahoo is also an openid provider. In the next article we shall see how to implement a Login with Yahoo feature using the same approach as used over here.
Failed to connect to 2a00:1450:4001:80a::1014: Network is unreachable
Hi
This is awesome, same as i want it.
but have a small issue with the user first name and last name.
did not get the user first name and last name after login from google.
Any help?
Thanks in advance.
Tejas