Ionic + Parse

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



·

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).

  • Pingback: User Authentication with Parse in an Ionic Application | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Pingback: Email and Facebook Authentication with Ionic and Firebase | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • teshshah

    Thank you! Bonus points for getting this to work on browser AND mobile devices.

    • Daniel Hanke

      forgive me my old post – yes, big Bonus!:)

  • Arndt Knoetze

    man you saved my life by this artickle , was strugeling with this a long time. Thnaks so much for your work!

  • Chong Sheng Hou

    Hi I need help…

    Im still getting Invalid key hash from facebook. Already generated the key hash with the following and enter the value to facebook developer website:

    keytool -exportcert -alias androiddebugkey -keystore “C:UsersSheng Hou.androiddebug.keystore” | “C:UsersSheng Houopenssl-0.9.8k_X64binopenssl.exe” sha1 -binary | “C:UsersSheng Houopenssl-0.9.8k_X64binopenssl.exe” base64

  • James

    how would you get the email and name of the person from facebook then save it to parse?

  • Matheus

    Great post, Josh. It worked like a charm for me. However, did you get any issues about screen permission using native facebook app? In iOS it did not showed for me. The facebook app is open and then iOS get back to my app without any questions to the user.

    • Hi Matheus. Did you ever sort out your problem? I am experiencing something very similar.

      • Matheus

        Hey Bill, at that time I built myself the permission popup to user. I think that was a iOS 9 issue of Facebook app with the new iOS 9, I dunno. Because, right now, with the most recent version of iOS and Facebook that issue is gone. Now, the permission screen from Facebook shows, but be aware that after the first time the user authorizes the screen won’t appears anymore.

      • Speedy reply! Yes I think it is the fact I have already authorised on my iPhone that makes it look odd. Do you get two visits to the FB screen one for cordovaFacebook and one for the parse login? I have used the tutorial code exactly as is so would imagine someone else must be experiencing the same thing.

      • Matheus

        Yeah, You caught me in the right time! But about the using of Parse, I’ll not be able to help you. I was using my own backend to retrieve user’s data from FB.

  • Hey i am not getting name and Email id? how to get that?

    • Alaa Alshaikh

      Parse.FacebookUtils.logIn(“user_likes,email”, {
      success: function(user) {
      // Handle successful login
      },
      error: function(user, error) {
      // Handle errors and cancellation
      }
      });

  • teddy

    Great work! Keep it up! This is the detailed tutorial ever.

  • Állif Henrique

    Work for mobile? Because only in the browser and did not work on mobile

  • Francisco Vieira

    where this .Parse(…) function comes from????

  • Azlan Jamal

    Hi, thanks a million for this. Life saving. It worked. Thanks bro.

  • Really good tutorial! I spent a lot of time trying to get this working before finding this. Saved my sanity! It all works fine for me except doing both $cordovaFacebook.login and
    Parse.FacebookUtils.logIn causes two visits to the Facebook blue screen. Is there anyway to stop Parse.FacebookUtils.logIn flashing the FB blue screen? When testing this on my iPhone the user never gets asked to approve so what they see is a sequential flash app->FB->app->FB->app which is very irritating 🙂 Any suggestions you have would be gratefully received 🙂

  • Pablo Fallas

    THhis is not working for me I got an error saying that it can’t find the Facebook plugin. I also tried the very first part in this article and works like a charm in a browser but when I try to do it in Ionic View the authorization page never shows up ? any Idea how I can solve this issue ?

  • Mark Duffy

    Hi, can someone tell me how to grab the users email address from facebook at login? Also, populate the parse username field with that also.

  • mahadev

    Hello Josh, how to store facebook user email, profile name and picture in parse user details section.

  • Ladna Meke

    Nice tut. However, I think if( (ionic.Platform.isIOS() || ionic.Platform.isAndroid())){} isn’t a good way to check if running in device. This returns true even if running in ionic lab. A better way would be if (!window.cordova){}. A more ionic-ey way would be if(!ionic.Platform.isWebView()){}

  • Ajay Saini

    i am facing a problem whenever i login with facebook. is showing invalid key hash.the key hash +H9|4+vy************ does not match any stored key hash. how to fix it

  • Pingback: Installing ngCordova in an Ionic Application | HTML5 Mobile Tutorials | Ionic, Phaser, Sencha Touch & PhoneGap()

  • Ajay Saini

    when i try to ligin with facebook it show this error “invalid hash key. the key hash +H9I4+vy46g8……..does not match any stroed kay hash ” how to fix it ?