Add Firebase authentication to your Ionic App

by - April 02, 2017

Authentication it’s a huge component on your app, it will enable to build a relation with your users.
Sign up or RegistrationSign in or Log in, you name it, these are the key to engage with them. Whether you want users to participate by leaving comments, blogging, leaving messages onto boards, posting photos or creating a personal network - participation needs to be attributed and/or associated with the user for purposes of building community and reputation, or creating a personal profile or knowledge base. This is why is imperative to build authentication the right way!

Authentication options

We have covered this in other posts, but here we go again, when it comes to authentication there are some different approaches that you can take.

The handcrafted way (using your own API/Backend)

If you already have an API or backend that handles user authentication, or if you are one of those who like to have strict control over the backend implementation, then this may be your option.
Mobile apps require a different authentication strategy than web apps or websites. You don’t have sessions so you have to go with the token based authentication. For my clients production apps I use Strongloop. Basically is a platform that enables you to easily build custom API’s for your backend needs. It comes with token based authentication and an AngularJS SDK that works smoothly in your Ionic app. (You can read more about how easy is to manage users in this post “Managing users with StrongLoop”)

The Social way

You can chose to provide authentication using well known social networks such as Facebook, Instagram, TwitterGoogle, etc.

The BaaS way

If you don’t want to take care of your own infrastructure and backend implementation, then there are a few BaaS (Backend aa Service) or PaaS (Platform aa Service) options.
Ionic Platform is a great service launched recently by the creators of Ionic Framework. We have wrote about it in the past, and I can say it’s an elegant solution that will help you setup your app’s user authentication with ease.
Some time ago, Google acquired Firebase. Its platform was already great, but it’s been getting better since adding tons of features and integrations. Firebase is also a BaaS solution with very useful services to build mobile apps. It offers cool features among analyticsuser authenticationcloud messagingrealtime database, and notifications.
Each option has it’s benefits, and of course you can mix them together and have an even more complete solution and experience for your users. It’s very common to see different ways of authenticating users within an app.
Social + BaaS or Social + your own API are very common nowadays
In this article we’ll explore how to add Firebase authentication services to your Ionic App.
There are two different authentication options. First, we will be implementing the old school way, by configuring user and password. Afterwords, we’ll see how to implement authentication by using the Firebase Facebook Provider.
This way we’ll teach you a Social + BaaS approach using Firebase platform.

BaaS/PaaS Authentication

Advantages of using a PaaS:
  • Update and Maintenance of database tools, systems and infrastructure is full responsibility of the PaaS Cloud Provider.
  • Some PaaS providers give developing options to multiple platforms: mobile, browser, among others.
  • Cloud PaaS provider can deliver better security measures than the existing software.
  • There’s no need to manage the introduction of new versions of software or code. This is handle by the Cloud PaaS provider.
  • There’s no need to supply servers or to manage the underlying data center.
Without further ado, let’s get our hand on with the code!

Hands on!

Remember ... you will need:

    • An Ionic App where you will integrate this login. You can either use a blank app, or an existing one. If you don’t have one, you should check out these beautiful templates that’ll save you time.
    • Firebase account. If you haven't created a Firebase account yet, you will need to sign upfor one.
    • Facebook developer account. If you haven't done this yet, create one here.
Ad time!: If you are looking for a beautiful starter app with login UI/UX you must have a look at our beautiful mobile themes, templates & components. Specially our Logins Category.

Setup

Install Ionic

You can find the Ionic official installation documentation here.
Follow these links if you want more information on how to get started:

Other Dependencies

We are also going to use two javascript client side libraries:
  • The Firebase web client [firebase.js]
    We are using firebase.js v2.4.2 which is the last version before the new firebase v3.x
  • And the AngularJS bindings for Firebase [angularfire.js]
    We will use angularfire.js v1.2.0 which is the last version compatible with firebase.js v2.x.x. The new angularfire.js v2.x.x depends on firebase.js v3.x.x
Add these dependencies to your bower.json file and then run bower install to install them. You can also run bower install firebase#2.4.2 --save and bower install angularfire#1.2.0 --saveand that will handle the installation also.

Step 1: Set up your Firebase App

First of all, we should create a Firebase account and create a new Firebase app. As we mentioned before, we are going to use Firebase as our PaaS. It’s gonna be our data store where we will save all the users data.
We will use this Firebase App both for the email / password and Facebook authenticationproviders options.

Step 2: Register your Facebook app

In this section I will help you configuring your Facebook app.
Once in the dashboard, create a new Facebook App for our project. As we are going to implement the javascript facebook authentication, you should choose the website platform when creating the new facebook app.

NOTE: If you are looking for the native facebook authentication, please consider looking at this post: Add Facebook Native Login to your Ionic App. There you can also find more information about the differences between Facebook javascript and native approaches for authentication.

After creating the Facebook app, you should go to the app’s dashboard and on the Products section click Add Product to add the Facebook Login product.

Afterwards, we only need to add a redirection URL under the Valid OAuth redirect URIs field. The redirection URL should be like this https://auth.firebase.com/v2//auth/facebook/callback.
Use the Firebase app name from the Step 1.

Just a little thing before going to the next step. Go to Settings and grab your App ID and App Secretfrom your Facebook app. We will need them later on in this tutorial.

NOTE: If you need further help for these configurations, please check this section of the Firebase documentation.

Step 3: Hook up our Facebook App with Firebase

After having both Firebase and Facebook apps ready we need to set up some minor stuff before proceeding to code.
Go to your Firebase dashboard and under the Login & Auth select the Facebook tab and paste your Facebook App ID and App Secret you got on Step 2.

Step 4: Ionic Templates & AngularJS Navigation

Now we will go straight to the code, so open your Ionic App with your preferred code editor. Personally we recommend atom.
For this example app we are going to build a simple navigation schema that will enable us to have both login and signup views, and only one authenticated view with the user profile information. This user view will be only accessible for authenticated users.
This image illustrates the navigation between views


The app will have two main nodes in the navigation, one for the auth section of the app (loginsignup views) and other for the main section of the app (user view).


To achieve this, we are going to:
  • Use www/index.html with default app’s placeholder
  • Add www/templates/auth/login.html with basic login view layout
  • Add www/templates/auth/signup.html with basic signup view layout
  • Add www/templates/app/user.html with basic user details view layout
  • Update www/js/app.js with app’s desired routing
  • Update www/js/controllers.js with a basic controllers for each view

App Placeholder (index.html)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <!-- compiled css output -->
    <link href="css/ionic.app.css" rel="stylesheet">

    <!-- vendor js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>
    <script src="lib/firebase/firebase.js"></script>
    <script src="lib/angularfire/dist/angularfire.min.js"></script>

    <!-- cordova script (this will be a 404 during development) -->
    <script src="cordova.js"></script>

    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>
  </head>
  <body ng-app="starter">
    <ion-nav-view name="main-view"></ion-nav-view>
  </body>
</html>

App Routes (js/app.js)

.config(function($stateProvider, $urlRouterProvider) {

  // Ionic uses AngularUI Router which uses the concept of states
  // Learn more here: https://github.com/angular-ui/ui-router
  // Set up the various states which the app can be in.
  // Each state's controller can be found in controllers.js
  $stateProvider

  // setup an abstract state for the auth section
  .state('auth', {
    url: '/auth',
    abstract: true
  })

  .state('auth.login', {
    url: '/login',
    views: {
      'main-view@': {
        templateUrl: 'templates/auth/login.html',
        controller: 'LogInCtrl'
      }
    },
    data: {
      authenticate: false
    }
  })

  .state('auth.signup', {
    url: '/signup',
    views: {
      'main-view@': {
        templateUrl: 'templates/auth/signup.html',
        controller: 'SignUpCtrl'
      }
    },
    data: {
      authenticate: false
    }
  })

  .state('app', {
    url: '/app',
    abstract: true
  })

  .state('app.user', {
    url: '/user',
    views: {
      'main-view@': {
        templateUrl: 'templates/app/user.html',
        controller: 'UserCtrl'
      }
    },
    data: {
      authenticate: true
    }
  })

  ;

  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/app/user');
})
Serve the app with ionic serve and look at the Ionic dev server running on http://localhost:8100. There you’ll see the Ionic Auth app with the navigation DONE.

Step 3: Login/Signup Layout

The layout of login and signup views is very similar and simple. Note that we added basic angular.jsform validation using ng-disabled directive and required attributes on the fields just to prevent the submission of the form if the fields are empty.

Login View (templates/auth/login.html)

<ion-view>
  <ion-header-bar class="bar-stable">
    <h1 class="title">Log In</h1>
  </ion-header-bar>
  <ion-content class="padding">
    <button class="button button-block button-positive" ng-click="facebookLogin()">Login with Facebook</button>
    <div class="row">
      <div class="col col-center">
        <h4 class="text-center">OR</h4>
      </div>
    </div>
    <form name="login_form" class="form-container" novalidate>
      <div class="list list-inset">
        <label class="item item-input">
          <input type="email" placeholder="Email" ng-model="user.email" required>
        </label>
        <label class="item item-input">
          <input type="password" placeholder="Password" ng-model="user.password" required>
        </label>
      </div>
      <button class="button button-block button-balanced" ng-click="login(user)" ng-disabled="login_form.$invalid">Login</button>
    </form>
    <button class="button button-block button-clear button-positive button-small" ui-sref="auth.signup">
      Sign Up
    </button>
    <div ng-show="errors">
      <p class="message error" ng-repeat="error in errors"><b>[{{error.code}}]</b> {{error.msg}}</p>
    </div>
  </ion-content>
</ion-view>

Signup View (templates/auth/signup.html)

<ion-view>
  <ion-header-bar class="bar-stable">
    <h1 class="title">Sign Up</h1>
  </ion-header-bar>
  <ion-content class="padding">
    <form name="signup_form" class="form-container" novalidate>
      <div class="list list-inset">
        <label class="item item-input">
          <input type="email" placeholder="Email" ng-model="data.email" required>
        </label>
        <label class="item item-input">
          <input type="password"  placeholder="Password" ng-model="data.password" required>
        </label>
      </div>
      <button class="button button-block button-energized" ng-click="signup(data)" ng-disabled="signup_form.$invalid">Create Account</button>
    </form>
    <button class="button button-block button-clear button-positive button-small" ui-sref="auth.login">
      Already have an account?
    </button>
    <div ng-show="errors">
      <p class="message error" ng-repeat="error in errors"><b>[{{error.code}}]</b> {{error.msg}}</p>
    </div>
  </ion-content>
</ion-view>

Step 4: App Controllers & Authentication Implementation

The angular.js controllers are the bridge between the views and the main functionality of the app. In this case we choose to add another layer of abstraction. Instead of having the authentication functionality directly in the controllers, we added an AuthService that handles all the authentication communication between our app and the Firebase services.
This way we add independence and if you want to have another provider for the authentication you just have to add a new service with the same signature methods and forget about changing the controllers.

App Controllers (js/controllers.js)

.controller('LogInCtrl', function($scope, $state, AuthService, $ionicLoading) {
  $scope.login = function(user){
    $ionicLoading.show({
      template: 'Logging in ...'
    });

    AuthService.doLogin(user)
    .then(function(user){
      // success
      $state.go('app.user');
      $ionicLoading.hide();
    },function(err){
      // error
      $scope.errors = err;
      $ionicLoading.hide();
    });
  };

  $scope.facebookLogin = function(){
    $ionicLoading.show({
      template: 'Logging in with Facebook ...'
    });

    AuthService.doFacebookLogin()
    .then(function(user){
      // success
      $state.go('app.user');
      $ionicLoading.hide();
    },function(err){
      // error
      $scope.errors = err;
      $ionicLoading.hide();
    });
  };
})

.controller('SignUpCtrl', function($scope, $state, AuthService, $ionicLoading) {
  $scope.signup = function(user){
    $ionicLoading.show({
      template: 'Signing up ...'
    });

    AuthService.doSignup(user)
    .then(function(user){
      // success
      $state.go('app.user');
      $ionicLoading.hide();
    },function(err){
      // error
      $scope.errors = err;
      $ionicLoading.hide();
    });
  };
})

.controller('UserCtrl', function($scope, $state, AuthService){
  $scope.current_user = {};

  var current_user = AuthService.getUser();

  if(current_user && current_user.provider == "facebook"){
    $scope.current_user.email = current_user.facebook.displayName;
    $scope.current_user.image = current_user.facebook.profileImageURL;
  } else {
    $scope.current_user.email = current_user.password.email;
    $scope.current_user.image = current_user.password.profileImageURL;
  }

  $scope.logout = function(){
    AuthService.doLogout();

    $state.go('auth.login');
  };
})

Authentication service using Firebase (js/services.js)

.service('AuthService', function($q){
  var _firebase = new Firebase("https://logfirebase.firebaseio.com/");

  this.userIsLoggedIn = function(){
    var deferred = $q.defer(),
        authService = this,
        isLoggedIn = (authService.getUser() !== null);

    deferred.resolve(isLoggedIn);

    return deferred.promise;
  };

  this.getUser = function(){
    return _firebase.getAuth();
  };

  this.doLogin = function(user){
    var deferred = $q.defer();

    _firebase.authWithPassword({
      email    : user.email,
      password : user.password
    }, function(errors, data) {
      if (errors) {
        var errors_list = [],
            error = {
              code: errors.code,
              msg: errors.message
            };
        errors_list.push(error);
        deferred.reject(errors_list);
      } else {
        deferred.resolve(data);
      }
    });

    return deferred.promise;
  };

  this.doFacebookLogin = function(){
    var deferred = $q.defer();

    _firebase.authWithOAuthPopup("facebook", function(errors, data) {
      if (errors) {
        var errors_list = [],
            error = {
              code: errors.code,
              msg: errors.message
            };
        errors_list.push(error);
        deferred.reject(errors_list);
      } else {
        deferred.resolve(data);
      }
    });

    return deferred.promise;
  };

  this.doSignup = function(user){
    var deferred = $q.defer(),
        authService = this;

    _firebase.createUser({
      email    : user.email,
      password : user.password,
    }, function(errors, data) {
      if (errors) {
        var errors_list = [],
            error = {
              code: errors.code,
              msg: errors.message
            };
        errors_list.push(error);
        deferred.reject(errors_list);
      } else {
        // After signup we should automatically login the user
        authService.doLogin(user)
        .then(function(data){
          // success
          deferred.resolve(data);
        },function(err){
          // error
          deferred.reject(err);
        });
      }
    });

    return deferred.promise;
  };

  this.doLogout = function(){
    _firebase.unauth();
  };
})

Remember we mentioned earlier that we were going to implement both email / password and facebook authentication options using Firebase providers. In Step 3 we hook up everything to enable the Firebase Facebook auth provider.
Now it’s time to implement the code for that.
We are also going to put it in the AuthService to gain abstraction in our code. This is how it looks like:

Authentication service using Firebase Facebook provider (js/services.js)

this.doFacebookLogin = function(){
  var deferred = $q.defer();

  _firebase.authWithOAuthPopup("facebook", function(errors, data) {
    if (errors) {
      var errors_list = [],
          error = {
            code: errors.code,
            msg: errors.message
          };
      errors_list.push(error);
      deferred.reject(errors_list);
    } else {
      deferred.resolve(data);
    }
  });

  return deferred.promise;
};

Step 5: Final details

The last thing we are going to do is to add some code in the app.js file to handle only the authentication views. We will check if the view state requires authentication and if the user is authenticated and based on that decide whether or not show him the view.

App run method (js/app.js)

.run(function($ionicPlatform, $rootScope, $state, AuthService) {
  $ionicPlatform.ready(function(){
    AuthService.userIsLoggedIn().then(function(response)
    {
      if(response === true)
      {
        $state.go('app.user');
      }
      else
      {
        $state.go('auth.login');
      }
    });

    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if (window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
      cordova.plugins.Keyboard.disableScroll(true);
    }
    if (window.StatusBar) {
      // org.apache.cordova.statusbar required
      StatusBar.styleDefault();
    }
  });

  // UI Router Authentication Check
  $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
    if (toState.data.authenticate)
    {
      AuthService.userIsLoggedIn().then(function(response)
      {
        if(response === false)
        {
          event.preventDefault();
          $state.go('auth.login');
        }
      });
    }
  });
})

You May Also Like

0 comments