Social Network Authentication: Google+

Tweet
This entry is part 2 of 4 in the series Using Social Networks as a Login System

Using Social Networks as a Login System

In the previous part of this series, we created our initial interfaces which we’ll be using for all future parts. In this article, we will integrate Google+ within our application.

You can view the code for all the articles on this Github page.

Google+ login

We start off by creating the following directory: src/SitePoint/SocialLogin/Google. This will be the directory we will be working in for the rest of the article. Within this directory, we create the GoogleLogin class. This class implements the SocialLoginInterface interface which we created in the previous article. Make sure a property called service is available to store our service in (see code below).

Google has some specific needs to make sure we can log in. So make sure we have the following 3 properties present in the class:
– client id
– key
– callback URL

Since all 3 properties are required for our application to work, our constructor will receive these 3 variables as parameters and set the properties.

Until now, our code looks like this.

<?php

namespace SitePoint\SocialLogin\Google;

use SitePoint\SocialLogin\Interfaces\SocialLoginInterface;

class GoogleLogin implements SocialLoginInterface {

    /**
     * Google service
     *
     * @var string
     */
    protected $service;

    /**
     * OAuth client ID
     *
     * @var string
     */
    protected $clientId;

    /**
     * OAuth key
     *
     * @var string
     */
    protected $key;

    /**
     * Callback url
     *
     * @var string
     */
    protected $callbackUrl;

    /**
     * Constructor
     *
     * @param $clientId string
     * @param $key string
     * @param $callbackUrl string
     */
    public function __construct($clientId, $key, $callbackUrl)
    {
        $this->clientId = $clientId;
        $this->key = $key;
        $this->callbackUrl = $callbackUrl;
    }

    /**
     * Initializes our service
     */
    public function init()
    {

    }

    /**
     * Returns the login url for the social network
     *
     * @return string
     */
    public function getLoginUrl()
    {
    }

    /**
     * Handles the login callback from the social network
     *
     * @param string $accessCode
     *
     * @return SocialUserInterface
     */
    public function loginCallback($accessCode)
    {

    }
}

The next step is to create our Google+ service. As you have seen in the previous article, we are using an OAuth library to set up our connection with Google+. We will be using our init method to create the service.

Before we can start, we have to add some use statements to the top of our class.

use OAuth\ServiceFactory;
use OAuth\OAuth2\Service\Google;
use OAuth\Common\Storage\Session;
use OAuth\Common\Consumer\Credentials;

Everything for our init method is now present. Time to set up our connection with Google+. We do this with the basic examples of the OAuth library we are using.

    /**
     * Initializes our service
     */
    public function init()
    {
        $storage = new Session();
        $serviceFactory = new ServiceFactory();
        $credentials = new Credentials($this->clientId, $this->key, $this->callbackUrl);
        $this->service = $serviceFactory->createService('google', $credentials, $storage, array('userinfo_email', 'userinfo_profile'));

        return $this;
    }

Our service is set now, so we can continue by filling out other methods from the interface. We will start off with the getLoginUrl method. This method will return a URL which you will be redirecting your user to.

/**
 * Returns the login url for the social network
 *
 * @return string
 */
public function getLoginUrl()
{
    return $this->service->getAuthorizationUri();
}

The next method to define is the loginCallback method. When a user accepts Google+ sharing data with your application, the user will be redirected back to the url you defined as the callbackUrl. When the user is requesting that URL, you should call the loginCallback method from this class.

If everything went well, we can now retrieve data from Google+.

/**
 * Handles the login callback from the social network
 *
 * @return SocialUserInterface
 */
    public function loginCallback()
    {
        $userData = json_decode($this->service->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
        $googleUser = new GoogleUser($userData);
        return $googleUser;
    }

You might notice we are using a class named GoogleUser here and we are returning a class with the interface SocialUserInterface. However, this class is not yet present.

Google+ user

Every social network returns user data in a different way. Instead of just returning this data, we are going to normalize it so we can easily work with it. We need to create a class named GoogleUser. This class will implement the SocialUserInterface we created in the previous article.

The constructor of this class expects the raw data of Google in this case. In every getter, we will retrieve the data we want from the raw data from Google. In the end, your GoogleUser class could look like this.

<?php

namespace SitePoint\SocialLogin\Google;

use SitePoint\SocialLogin\Interfaces\SocialUserInterface;

class GoogleUser implements SocialUserInterface {

    /**
     * @var mixed user data
     */
    private $userData;

    /**
     * Constructor.
     *
     * @param $userData mixed Raw social network data for this particular user
     */
    public function __construct($userData)
    {
        $this->userData = $userData;
    }

    /**
     * Get the provider name
     *
     * @return string
     */
    public function getProvider()
    {
        return "google";
    }

    /**
     * Get the UID of the user
     *
     * @return string
     */
    public function getUid()
    {
        if(array_key_exists('id', $this->userData)) {
            return $this->userData['id'];
        }
        return null;
    }

    /**
     * Get the first name of the user
     *
     * @return string
     */
    public function getFirstname()
    {
        if(array_key_exists('given_name', $this->userData)) {
            return $this->userData['given_name'];
        }
        return null;
    }

    /**
     * Get the last name of the user
     *
     * @return string
     */
    public function getLastname()
    {
        if(array_key_exists('family_name', $this->userData)) {
            return $this->userData['family_name'];
        }
        return null;
    }

    /**
     * Get the username
     *
     * @return string
     */
    public function getUsername()
    {
        if(array_key_exists('family_name', $this->userData)) {
            return str_replace(" ", "_", $this->userData['family_name']);
        }

        return null;
    }

    /**
     * Get the emailaddress
     *
     * @return string
     */
    public function getEmailAddress()
    {
        if(array_key_exists('email', $this->userData)) {
            return $this->userData['email'];
        }
        return null;
    }

    /**
     * Get the city
     *
     * @return string
     */
    public function getCity()
    {
        return null;
    }

    /**
     * Get the birthdate
     *
     * @return string
     */
    public function getBirthDate()
    {
        return null;
    }

    /**
     * Get the gender
     *
     * @return string
     */
    public function getGender()
    {

        if(array_key_exists('gender', $this->userData)) {
            return $this->userData['gender'];
        }
        return null;
    }
}

Implementing it into your application

Depending on the framework you are using (if you are using any of course) the implementation can be slightly different. However, I would recommend you have methods like the following below.

public function loginForm() 
{
    //Renders a login form with a button to login with google+ (calls the LoginWithGoogle method).
}
public function loginWithGoogle()
{
    $googleLogin = new GoogleLogin("clientId", "key", "url/loginCallbackGoogle");
    $googleUrl = $googleLogin->init()->getLoginUrl();
    // Redirect user to $googleUrl.
}
public function loginCallbackGoogle()
{
    $googleLogin = new GoogleLogin("clientId", "key", "url/loginCallbackGoogle");
    $googleUser = $googleLogin->init()->loginCallback();
    // You can now either save or merge this user.
    // Next, you can log the user in and redirect him to any page you like
}

Recommendation 1:

Instead of a link in your log in form which redirects you to the loginWithGoogle method, you could also place the Google URL into your view. However, note that you are already creating a connection with Google. This could slow down your page. Especially when using multiple social networks. In general, I would recommend the way above.

Recommendation 2:

If you are going to use multiple social logins, you could easily add a parameter named $socialNetwork to the loginWith and the loginCallback method and add a switch statement inside.

Quick test

If you want to run a quick test to try out the code, clone the Github repository to your local computer and switch to the branch named part2. Fill in your Google+ API details within the testGoogle.php file and run the file from within your browser. When requesting the page, you will be redirected to a Google page, requesting you to share information.

Google requesting to share information

When clicking accept, you will be redirected back to the page you configured as callback URL, showing your first and last name.

Conclusion

In this article we took the base we set in the previous article and worked further on that. We implemented a way to log in with Google+ and made sure we returned an object which normalizes the data you received from Google. In the upcoming article, we will have a look at how we can merge accounts when people are using multiple logins. In follow up articles we will take a look at how we add more social networks to our system.

Using Social Networks as a Login System

<< Social Network Authentication: SetupSocial Network Authentication: Merging Accounts >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Taylor Ren

    I am looking forward to a Twitter interface. Come on, pal!

    • https://www.peternijssen.nl/ Peter Nijssen

      Thanks. That one will be covered in Part 4, which will be published a bit later. However, I’ll update the git repo later today or tomorrow :)

  • http://www.7eyetechnologies.com Kristina Roy

    This is a great post for Google+ social authentication that is very helpful to php developers. Thanks to share this useful code with us.

    • https://www.peternijssen.nl/ Peter Nijssen

      Thanks @kristinaroy:disqus!

      Later today the third part will be published, in which I talk about how you can merge social accounts with your own user accounts. Later on, a fourth article is coming through, showing Twitter and Facebook login.

  • https://www.peternijssen.nl/ Peter Nijssen

    Hi.

    Thanks! As far as I currently see, the interface is correctly implemented. Which part do you believe is incorrect?

    • blatantlyobvious

      It was actually me who posted this, tried to delete it when I saw that it was just my mistake :) One thing though, in the loginCallbackGoogle() method you aren’t passing $accessCode like you are on Github — $this->service->requestAccessToken($accessCode); (unless I missed it)

      • https://www.peternijssen.nl/ Peter Nijssen

        Well noticed :) You can sent it with it, so you can do some additional checks, but it should not be needed to make it run.

  • blatantlyobvious

    Hi Peter,

    When attempting to test the G+ login with the file you provided, I’m getting an error:

    Uncaught exception
    ‘OAuthCommonStorageExceptionTokenNotFoundException’ with message
    ‘Token not found in session, are you sure you stored it?’

    Any ideas? Thanks a lot!

    • https://www.peternijssen.nl/ Peter Nijssen

      Hi,

      Hm… I tested it before I pushed it. You are certain your credentials are correct?

  • gamal anwar

    Hi Peter,

    While testing i get this error after comming bak from google+:

    Fatal error: Uncaught exception ‘OAuthCommonHttpExceptionTokenResponseException’ with message ‘ in F:xampphtdocssocial-loginvendorlusitanianoauthsrcOAuthCommonHttpClientStreamClient.php on line 70