Social Network Authentication: Twitter and Facebook

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

Using Social Networks as a Login System

In the previous parts of this series, we created our initial interfaces, set up our Google+ login functionality and talked about how we can merge our accounts together. In this article, we will integrate Twitter and Facebook within our application. You will see a lot of similarities with the Google+ article, so if you could follow that one easily, you won’t have much trouble with this one. If you haven’t read that article yet, I suggest you read it first before continuing this article.

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

Twitter authentication

Once again, we start off by creating the following directory: src/SitePoint/SocialLogin/Twitter. Within this directory, we create the TwitterLogin class. This class implements the SocialLoginInterface interface which we created in the first article. Make sure a property called service is available to store our service in.

Like Google+, Twitter 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.

Up until now, your code should look the same as the first example in the Google+ article.

Our next step is to create our Twitter service. We will be using the same OAuth library and will set up a connection within the init method.

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

use OAuth\ServiceFactory;
use OAuth\OAuth1\Service\Twitter;
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 Twitter. 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('twitter', $credentials, $storage);

        return $this;
    }

Our service is set now, so we can now 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()
{
        $token = $this->service->requestRequestToken();
        return $this->service->getAuthorizationUri(array('oauth_token' => $token->getRequestToken()));
}

You might notice that this method (and the following one which we will discuss) is slightly different from the Google+ article. This is due to the fact that Google+ uses OAuth 2, while Twitter uses OAuth 1. Twitter was one of the first companies to use OAuth, since the original creator of OAuth was working at Twitter at that time. Twitter is still using OAuth 1, although they are making progress moving to OAuth 2.

The next method to define is the loginCallback method. When a user accepts Twitter 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 Twitter.

/**
 * Handles the login callback from the social network
 *
 * @return SocialUserInterface
 */
    public function loginCallback()
    {
        $storage = new Session();
        $token = $storage->retrieveAccessToken('Twitter');

        $this->service->requestAccessToken(
            $_GET['oauth_token'],
            $_GET['oauth_verifier'],
            $token->getRequestTokenSecret()
        );
        $userData = json_decode($this->service->request('account/verify_credentials.json'), true);
        $twitterUser = new TwitterUser($userData);
        return $twitterUser;
    }

This time we are using a class named TwitterUser here and we are returning a class with the interface SocialUserInterface. However, this class is not yet present. Time to create it!

Twitter user

Twitter returns the data back in a different way than Google+, so we have to normalize again. We need to create a class named TwitterUser which will implement the SocialUserInterface we created in the first article.

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

<?php

namespace SitePoint\SocialLogin\Twitter;

use SitePoint\SocialLogin\Interfaces\SocialUserInterface;

class TwitterUser 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 "twitter";
    }

    /**
     * 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('name', $this->userData)) {
            list($firstname, $lastname) = explode(" ", $this->userData['name']); // TODO: Of course you have to make this smarter
            return $firstname;
        }
        return null;
    }

    /**
     * Get the last name of the user
     *
     * @return string
     */
    public function getLastname()
    {
        if(array_key_exists('name', $this->userData)) {
            list($firstname, $lastname) = explode(" ", $this->userData['name']); // TODO: Of course you have to make this smarter
            return $lastname;
        }
        return null;
    }

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

        return null;
    }

    /**
     * Get the emailaddress
     *
     * @return string
     */
    public function getEmailAddress()
    {
        return null;
    }

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

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

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

You might notice that we have a lot less information than with Google+. Twitter just doesn’t share as much info. For instance, we are missing the email address, while this is one of the most common fields you would like to have within your application.

The only thing you can do is, after a login with Twitter, ask the user to fill in his email address and save it to your database. Other than that, you can try to merge a Twitter account with an already given account like in the previous part of this series.

Facebook login

With the knowledge you gathered from the Google+ and Twitter explanation, it should be easy for you to set the Facebook version up. Here you have the full implementation for the login class.

<?php

namespace SitePoint\SocialLogin\Facebook;

use SitePoint\SocialLogin\Interfaces\SocialLoginInterface;
use OAuth\ServiceFactory;
use OAuth\OAuth2\Service\Facebook;
use OAuth\Common\Storage\Session;
use OAuth\Common\Consumer\Credentials;

class FacebookLogin implements SocialLoginInterface {

    /**
     * Facebook 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()
    {
        $storage = new Session();
        $serviceFactory = new ServiceFactory();
        $credentials = new Credentials($this->clientId, $this->key, $this->callbackUrl);
        $this->service = $serviceFactory->createService(
            'facebook',
            $credentials,
            $storage,
            array(
                Facebook::SCOPE_EMAIL,
                Facebook::SCOPE_USER_BIRTHDAY,
                Facebook::SCOPE_USER_LOCATION
            )
        );

        return $this;
    }

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

    /**
     * Handles the login callback from the social network
     *
     * @param string $accessCode
     *
     * @return SocialUserInterface
     */
    public function loginCallback($accessCode)
    {
        $token = $this->service->requestAccessToken($accessCode);

        // Send a request with it
        $userData = json_decode($this->service->request('/me'), true);
        $facebookUser = new FacebookUser($userData);
        return $facebookUser;
    }
}

What you might notice is that I defined some scopes when creating the Facebook service. By defining these scopes, I can get back some extra details from the user. In this case, I am also collecting the email address, birthday and location. Do note that the more data you gather, the more chance you have a user will refuse to log in with your app.

Facebook user

The user class is even easier and doesn’t contain any surprises. When done, it should look like this.

<?php

namespace SitePoint\SocialLogin\Facebook;

use SitePoint\SocialLogin\Interfaces\SocialUserInterface;

class FacebookUser 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 "facebook";
    }

    /**
     * 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('first_name', $this->userData)) {
            return $this->userData['first_name'];
        }
        return null;
    }

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

    /**
     * Get the username
     *
     * @return string
     */
    public function getUsername()
    {
        if(array_key_exists('name', $this->userData)) {
            return str_replace(" ", "_", $this->userData['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()
    {
        if(array_key_exists('location', $this->userData)) {
            return $this->userData['location'];
        }
        return null;
    }

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

    /**
     * Get the gender
     *
     * @return string
     */
    public function getGender()
    {
        if(array_key_exists('location', $this->userData)) {
            return $this->userData['location']['name'];
        }
        return null;
    }
}

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 part4. Within this branch, you will see a testTwitter.php and testFacebook.php file. You can fill in the API details from both services and run the file within your browser. When requesting the page, you will be redirected to the social media page, requesting you 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 articles and worked further on that. Next to Google+, we can now also log in with Twitter and Facebook. In all cases, we return a normalized user object, which we can add to our own user system. With this article, we’ve come to the end of this series. Did it give you some insight in how to deal with social networks or framework agnostic packages? Feel free to ask any questions in the comments below!

Using Social Networks as a Login System

<< Social Network Authentication: Merging Accounts

Free JavaScript: Novice to Ninja Sample

Get a free 32-page chapter of JavaScript: Novice to Ninja and receive updates on exclusive offers from SitePoint.

  • aspraveen

    Great Article Series.. thanks for your time

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

      Thanks for reading and commenting :)

      • ConnieRWright

        Start working at home with>>CLICK NEXT TAB FOR MORE INFO AND HELP

  • gopalindians

    Peter ,you are awesome,rocking tut.

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

      Lol. Thanks for your kind words :)

  • rosy lily

    I get paid over $87 per hour working from home with 3 kids at home. I never thought I’d be able to do it but my best friend earns over 10k a month doing this and she convinced me to try. The potential with this is endless.

    Here ­­­­­­­­­is ­­­­­­­­­I ­­­­­started>>>>>>>>>➜➜➜➜➜➜➜

    ➜➜➜➜ W­W­W­.­P­A­Y­R­A­P­.ℭ­ℴ­m

    —————————————————–

    GO TO THE SITE –>>>CLICK NEXT TAB FOR MORE INFO AND HELP

  • Cherif BOUCHELAGHEM

    Nice work, God bless you :)

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

      Thanks!

  • http://www.i-visionblog.com s.shivasurya

    nice work! :)

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

    You mean Social network authentication in general?

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

    Yeah I am not sure if I know what you are heading to. So let me try to give you some answers.

    Regarding a login system, this whole series has to be built on top of your own login system. So the security of your login system is in your own hands. I only give you short explanation how you can integrate social networks to your login system and how to merge duplicates.

    Regarding the social accounts themselves, you could indeed use fake accounts from twitter etc. I think twitter is one of the services that is less reliable, since it doesn’t allow you to return an emailaddress. (Of course you can always create a fake emailaddress). However, you can also implement a check to see if the emailaddress is “valid” in your eyes. I know some websites don’t accept hotmail.com addresses for example. You can always ask for a different emailaddress. (Although you are creating a bigger barrier then).

    Regarding eCommerce, I think you can use social login. In the end, the customer who logged in, has to pay through a payment mechanism. If he has been using fake details all the time, I am pretty sure his package won’t be delivered ;)
    Just make sure that, although you already retrieve a lot of data, you always give the customer the chance to edit it. I guess most customers will like the fact that they already got a prefilled form due to the fact the data was retrieved from Facebook.

    Does this answer some of your questions? Else feel free to throw in a new question ;)