User Authentication with Ionic 1.x and Parse, Part 2: Facebook Login

13 min read

Originally published July 13, 2015

Check out my advanced Ionic tutorials at eliteionic.com

In Part 1 we generated a new Ionic application, set up Parse, and created an email login system. You will need to have Parse set up in an Ionic application already to complete this tutorial, so I would recommend going back and taking a look at Part 1 if you haven't already.

Creating a new user using an email address with Parse was pretty easy, but it is quite common for applications to offer Facebook as a sign up option as well. Parse does support Facebook, and you can integrate it quite easily using code like this:

Parse.FacebookUtils.logIn(null, {
  success: function (user) {
    console.log(user);
    if (!user.existed()) {
      alert('User signed up and logged in through Facebook!');
    } else {
      alert('User logged in through Facebook!');
    }
  },
  error: function (user, error) {
    alert('User cancelled the Facebook login or did not fully authorize.');
  },
});

The only problem though is that this won't use the native Facebook application installed on the users device for authentication (if the user wants to log in with Facebook, chances are they have the app installed). To avoid using a web based login where the user will have to enter in their username and password, we want to instead switch them to the native Facebook application, have them press a button to allow our application access, and then redirect them back to our application.

To create this process we will need to use a PhoneGap plugin for authenticating with the native Facebook application. We can then pass the authentication information returned to the normal Facebook login provided by Parse and continue as normal.

Setting up a Facebook App

Before we even get started, you should first make a new Facebook App for your project at developers.facebook.com. This will allow you to get an APP ID which you will need to supply to the Facebook Connect plugin to allow you to interact with the Facebook API.

Create a new app by going to My Apps > Add a New App

Choose iOS as your platform

Facebook Add New App

Select Skip and Create App ID

New Facebook App

Give your application a name and then click 'Create App ID'

Facebook New App

Now you will be taken to the dashboard for your application where you will be able to retrieve your App ID – take note of this for later. Now you will need to add the iOS and Android platforms to your Facebook application.

Go to the Settings tab

Facebook App Settings

Click the + Add Platform button and choose iOS

Facebook Platform

Add your Bundle ID (which is specified in your config.xml file) and turn on Single Sign On and Deep Linking. Do the same for the Android platform and then hit Save Changes

Facebook Platform Settings

If you want to use this plugin on Android you will need to generate a key hash and add it to the Key Hashes field shown above. To do that you will need to use your keystore file for Android to generate the key hash using the following command:

keytool -exportcert -alias ALIAS_NAME -keystore KEYSTORE_FILE.keystore | openssl sha1 -binary | openssl base64

Installing the Facebook Connect Plugin

Before we get started, make sure you have added both the iOS and Android platforms to your project by running:

ionic platform add ios

and

ionic platform add android

We will be using ngCordova to implement the Facebook Connect plugin, if you're unfamiliar with ngCordova it is a collection of AngularJS services and extensions that make it easier to integrate Cordova plugins. Rather than calling a plugin manually, you can instead use the AngularJS service to do the work for you. Setting it up in your project is easy enough, just follow the following steps.

Run the following command to install ngCordova in your project

bower install ngCordova

Add a reference to the ngCordova library in your index.html

<script src="cordova.js"></script>
<script src="lib/ngCordova/dist/ng-cordova.js"></script>

Require ngCordova in the angular module for your application in app.js

angular.module('ionicApp', ['ionic', 'ngCordova']);

Inject the $cordovaFacebook plugin we are about to add into LoginCtrl in app.js

.controller('LoginCtrl', function($scope, $state, $cordovaFacebook) {

Now ngCordova is ready to use. The Facebook Connect plugin is a little trickier to install than most, due to a little quirk it has. If you try to install it directly from the Cordova Registry using a command like:

cordova plugin add pluginname

then it will break the included FacebookSDK.framework. So instead you will need to download a copy of the plugins repository and then install it manually.

NOTE: If you are using PhoneGap Build you will not need to worry about this, but you will have a slightly different process for installing the plugin. Go here for instructions and ignore the rest of this section.

Run the following command to download the Facebook Connect plugin repository into your project

git clone https://github.com/Wizcorp/phonegap-facebook-plugin.git

This does not install the plugin, it just creates a copy of it. To install the plugin you will need to run another command, and once again you're going to need to do something a little out of the ordinary.

Run the following command to install the Facebook Connect plugin

cordova -d plugin add phonegap-facebook-plugin --variable APP_ID="1234567891011" --variable APP_NAME="YourAppName"

You should replace the values for APP_ID and APP_ID with your own from the application you just created at developers.facebook.com. Once you have run that command Facebook Connect should be ready to use in your application.

Using Facebook Connect Authentication with Parse

As I mentioned before, we need to authenticate the user using the Facebook Connect plugin and then we need to handoff the information to Parse. To do that, we're going to create aloginFacebook() function.

Add a loginFacebook() function inside of LoginCtrl in app.js and add the following code:

$scope.loginFacebook = function () {
  $cordovaFacebook.login(['public_profile', 'email']).then(
    function (success) {
      console.log(success);
    },
    function (error) {
      console.log(error);
    }
  );
};

The code above will trigger the authentication process with Facebook Connect, requesting the "public_profile" and "email" permissions (you can read more about permissions here). Once the user has agreed to grant access to the application, the success function will trigger and all of the authentication data passed back will be contained within the success object.

Now we just need to pass some of that data through to Parse to trigger the rest of the Facebook sign up process. We simply pass the authentication data in as the first parameter of the logIn function, but Parse requires the data in a slightly different format so we will need to modify it a bit first (check out the documentation to see what I mean).

Modify the loginFacebook() function in app.js to reflect the following:

$scope.loginFacebook = function () {
  $cordovaFacebook.login(['public_profile', 'email']).then(
    function (success) {
      console.log(success);

      //Need to convert expiresIn format from FB to date
      var expiration_date = new Date();
      expiration_date.setSeconds(
        expiration_date.getSeconds() + success.authResponse.expiresIn
      );
      expiration_date = expiration_date.toISOString();

      var facebookAuthData = {
        id: success.authResponse.userID,
        access_token: success.authResponse.accessToken,
        expiration_date: expiration_date,
      };

      Parse.FacebookUtils.logIn(facebookAuthData, {
        success: function (user) {
          console.log(user);
          if (!user.existed()) {
            alert('User signed up and logged in through Facebook!');
          } else {
            alert('User logged in through Facebook!');
          }
        },
        error: function (user, error) {
          alert(
            'User cancelled the Facebook login or did not fully authorize.'
          );
        },
      });
    },
    function (error) {
      console.log(error);
    }
  );
};

Now after we have successfully retrieved the information we need from the Facebook Connect plugin, we create a new facebookAuthData object with the data converted into the format we need, and then we pass that data into Parse.FacebookUtils.logIn. With that code in place, you should now be able to successfully authenticate a user by triggering this function.

Of course, we don't have a button for that yet, so let's add one.

Modify your login.html template and add the following button:

<a
  class="button button-positive"
  style="width: 100%; margin: 20px 0;"
  ng-click="loginFacebook()"
>
  Facebook Login
</a>

You should now be able to successfully trigger a login with Facebook using the native app installed on your device, BUT, only if you are actually running it on a device. This will not work through the browser. If you want it to work through the browser as well (which is quite handy for development) there is a few more steps you will need to take.

If you do run it on a device though and look in your Parse dashboard now, you will see an entry for a user who signed up with Facebook:

Parse Backend

Enabling Login with Facebook through the Browser

If we want to enable logging in with Facebook through the browser then we will need to include the Facebook JavaScript SDK and trigger a slightly different function.

Modify the run method in app.js by adding following code:

Parse.initialize('YOUR APP ID', 'YOUR JAVASCRIPT KEY');

if (!(ionic.Platform.isIOS() || ionic.Platform.isAndroid())) {
  window.fbAsyncInit = function () {
    Parse.FacebookUtils.init({
      appId: 'YOUR FACEBOOK APP ID',
      version: 'v2.3',
      xfbml: true,
    });
  };

  (function (d, s, id) {
    var js,
      fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) {
      return;
    }
    js = d.createElement(s);
    js.id = id;
    js.src = '//connect.facebook.net/en_US/sdk.js';
    fjs.parentNode.insertBefore(js, fjs);
  })(document, 'script', 'facebook-jssdk');
}

We've added a check to see if we are running on a device where we would be using the native Facebook login, if not, we run the code to include the Facebook JavaScript SDK. You should of course replace the appId with your own Facebook App ID.

Now we just need to make a slight modification to the loginFacebook() function.

Modify the loginFacebook() function in app.js to reflect the following:

$scope.loginFacebook = function () {
  //Browser Login
  if (!(ionic.Platform.isIOS() || ionic.Platform.isAndroid())) {
    Parse.FacebookUtils.logIn(null, {
      success: function (user) {
        console.log(user);
        if (!user.existed()) {
          alert('User signed up and logged in through Facebook!');
        } else {
          alert('User logged in through Facebook!');
        }
      },
      error: function (user, error) {
        alert('User cancelled the Facebook login or did not fully authorize.');
      },
    });
  }
  //Native Login
  else {
    $cordovaFacebook.login(['public_profile', 'email']).then(
      function (success) {
        console.log(success);

        //Need to convert expiresIn format from FB to date
        var expiration_date = new Date();
        expiration_date.setSeconds(
          expiration_date.getSeconds() + success.authResponse.expiresIn
        );
        expiration_date = expiration_date.toISOString();

        var facebookAuthData = {
          id: success.authResponse.userID,
          access_token: success.authResponse.accessToken,
          expiration_date: expiration_date,
        };

        Parse.FacebookUtils.logIn(facebookAuthData, {
          success: function (user) {
            console.log(user);
            if (!user.existed()) {
              alert('User signed up and logged in through Facebook!');
            } else {
              alert('User logged in through Facebook!');
            }
          },
          error: function (user, error) {
            alert(
              'User cancelled the Facebook login or did not fully authorize.'
            );
          },
        });
      },
      function (error) {
        console.log(error);
      }
    );
  }
};

We've again added a similar check to see if we are running on a device, and if we are not we run the Parse logIn function without passing it auth data from Facebook manually (as Parse handles this automatically when using the browser based version). If we are running on a device then we just use the same method as before.

BONUS CONTENT: Grab the entire source code for this tutorial and the last by entering your email address below:

That's it! You should now have an application where you can either log in with Facebook or email, and if the application is running on a device it will even use the native Facebook app for authentication, meaning the user only has to push a single button to log in (and it will remember them next time).