Add Ionic Platform authentication to your Ionic App

by - April 02, 2017

Introduction

So, here’s the deal, Authentication is a key component for your app, it will enable you to build a relation with your users.
Sign up or Registration, Sign in or Log in, you name it, these are the key to engage with users. Whether you want users to participate by leaving comments, blogging, leaving messages onto boards, posting photos or building a personal network - participation needs to be attributed and/or associated with the user for purposes of building community and reputation, or building up a personal profile or knowledge base. You need 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 choose 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.
Firebase is a great service that will help you build your app’s backend with ease. (owned by Google). You can read more about this on this post "Logging Users In with Firebase".
The guys from Ionic recently launched the Ionic Platform which is a BaaS solution with very useful services to build mobile apps. This platform offers cool features among push notifications and user authentication.
In this post we will explore how to add Ionic Platform authentication services to your Ionic App.
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

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:

    • You need an Ionic app where you will integrate this login. You can either use a blank app, or an existing one.
    • An Ionic Platform account. If you haven't created an Ionic Platform account yet, you will need to sign up.
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 going to use the ionic-platform-web-client library to communicate our app with the Ionic Platform services (in our case the User Authentication service).
Add the dependency to your bower.json file and then run bower install to install it, or you can run ionic add ionic-platform-web-client and that will handle the installation also.

Step 1: Hook up our App with Ionic Platform

After following the setup you should have an ionic app with the dependencies installed. Now we need the Platform to assign your app a unique app id and app key. To do that, you should run the ionic io init command.
NOTE: The last step is critical, if we install the ionic-platform-web-client but don't init, then we may find some bugs when serving the app.
Finally, to see the current state of the project, run ionic serve

Step 2: Ionic Templates & AngularJS Navigation

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:
  • Add www/templates/auth/tabs.html with simple tabbed navigation layout for the login and signup views
  • 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/tabs.html with simple tabbed navigation layout for the user view
  • 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 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 tabs directive
  .state('auth', {
    url: '/auth',
    abstract: true,
    templateUrl: 'templates/auth/tabs.html'
  })

  // Each tab has its own nav history stack:
  .state('auth.login', {
    url: '/login',
    views: {
      'tab-login': {
        templateUrl: 'templates/auth/login.html',
        controller: 'LogInCtrl'
      }
    },
    data: {
      authenticate: false
    }
  })

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

  .state('app', {
    url: '/app',
    abstract: true,
    templateUrl: 'templates/app/tabs.html'
  })

  .state('app.user', {
    url: '/user',
    views: {
      'tab-user': {
        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');
})

Auth Tabbed Navigation Layout (templates/auth/tabs.html)

<ion-tabs class="tabs-icon-top tabs-color-active-positive">
  <!-- LogIn Tab -->
  <ion-tab title="Log In" icon-off="ion-person" icon-on="ion-person" ui-sref="auth.login">
    <ion-nav-view name="tab-login"></ion-nav-view>
  </ion-tab>

  <!-- SignUp Tab -->
  <ion-tab title="Sign Up" icon-off="ion-person-add" icon-on="ion-person-add" ui-sref="auth.signup">
    <ion-nav-view name="tab-signup"></ion-nav-view>
  </ion-tab>
</ion-tabs>

Main App Tabbed Navigation Layout (templates/app/tabs.html)

<ion-tabs class="tabs-icon-top tabs-color-active-positive">
  <!-- User Tab -->
  <ion-tab title="User" icon-off="ion-ios-body" icon-on="ion-ios-body">
      <ion-nav-view name="tab-user"></ion-nav-view>
  </ion-tab>
</ion-tabs>
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 view-title="Log In">
  <ion-content class="padding">
    <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-positive" ng-click="login(user)" ng-disabled="login_form.$invalid">Login</button>
    </form>
    <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 view-title="Sign Up">
  <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="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-positive" ng-click="signup(user)" ng-disabled="signup_form.$invalid">Create Account</button>
    </form>
    <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 Ionic Platform 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: 'Loging in ...'
    });

    AuthService.doLogin(user)
    .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 = Ionic.User.current();

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

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

Authentication service using Ionic Platform (js/services.js)

.service('AuthService', function($q, _) {
  this.userIsLoggedIn = function(){
    var deferred = $q.defer();

    deferred.resolve(Ionic.User.current().isAuthenticated());

    return deferred.promise;
  };

  this.doLogin = function(user) {
    var deferred = $q.defer(),
        authProvider = 'basic',
        authSettings = {
          'remember': true
        };

    Ionic.Auth.login(authProvider, authSettings, user)
    .then(function(data){
      deferred.resolve(data);
    }, function(errors){
      var errors_list = [];
      if(errors && errors.response && errors.response.responseText)
      {
        var json_response = JSON.parse(errors.response.responseText);

        if(json_response.error.details.length > 0) {
          _.each(json_response.error.details, function(err){
            var error = {
              code: json_response.meta.status,
              msg: err.errors[0]
            };
            errors_list.push(error);
          });
        }
        else {
          var error = {
            code: json_response.meta.status,
            msg: "An unexpected error has occurred"
          };
          errors_list.push(error);
        }
      }
      else if (errors && errors.length > 0){
        var error = {
          code: "unknown",
          msg: errors[0]
        };
        errors_list.push(error);
      }
      else {
        var error = {
          code: "unknown",
          msg: "An unexpected error has occurred"
        };
        errors_list.push(error);
      }

      deferred.reject(errors_list);
    });

    return deferred.promise;
  };

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

    Ionic.Auth.signup(user)
    .then(function(data){
      // After signup we should automatically login the user
      authService.doLogin(user)
      .then(function(data){
        // success
        deferred.resolve(data);
      },function(err){
        // error
        deferred.reject(err);
      });
    }, function(errors){
      var errors_list = [];
      if(errors && errors.response && errors.response.responseText)
      {
        var json_response = JSON.parse(errors.response.responseText);

        if(json_response.error.details.length > 0) {
          _.each(json_response.error.details, function(err){
            var error = {
              code: json_response.meta.status,
              msg: err.errors[0]
            };
            errors_list.push(error);
          });
        }
        else {
          var error = {
            code: json_response.meta.status,
            msg: "An unexpected error has occurred"
          };
          errors_list.push(error);
        }
      }
      else if (errors && errors.errors && errors.errors.length > 0){
        var error = {
          code: "unknown",
          msg: errors.errors[0]
        };
        errors_list.push(error);
      }
      else {
        var error = {
          code: "unknown",
          msg: "An unexpected error has occurred"
        };
        errors_list.push(error);
      }

      deferred.reject(errors_list);
    });

    return deferred.promise;
  };

  this.doLogout = function() {
    Ionic.Auth.logout();
  };
})





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 && window.cordova.plugins.Keyboard) {
      if (cordova.plugins.Keyboard.hideKeyboardAccessoryBar) {
        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