Writing the Ionic app

In the previous part of this blog we finished writing our nodejs server that exposes for time being a single auth route: /auth/facebook

In this part we will create a simple ionic app using their starter kits, and implement the authentication using an angular library called Satellizer created by a talented developer named Sahat Yalkabov you can follow him on his twitter account.

Github for this project: https://github.com/scopsy/node-ionic-jwt

Ionic

Ionic is a great framework to code hybrid applications that allows us to write apps with web languages: HTML, CSS, JS. One of the selling points for me was that Ionic is written on top of AngularJS. The combination between ionic and angular allowing developers to create scalable and fast hybrid apps.

The ionic ecosystem provides you with default navigation layouts and tools that will allow you to create and build applications easily.

Installations

First, we need to install cordova and ionic modules from npm. I would sugesst installing them globally.

npm install -g cordova ionic
Cordova?

cordova is the tool that allows you to turn your html and js to a bundled hybrid application which you can then upload to Google Store or Appstore.
It's also used as the bridge allowing you to interact with native phone API's such as: Camera, calendar, gyroscope, geolocation, battery etc...

There are a lot of cordova plugins you can use, and there is an awesome project named ngCordova that migrated most of the available cordova plugins in to an easy to integrate angular directives. You can check their site here.

So after installing ionic and cordova we can scaffold our app:

ionic start appName sidemenu

Note that we add sidemenu as the last argument, ionic comes with multiple starter layouts you can use. If you want clean app to start with simply change sidemenu to blank.

ionic cli will now download and install all the dependencies needed, when finished run ionic serve from cli. It will launch a web server on port 8100 (It is important that we will update our facebook developers console with the port we are launching the app from).

Installing satellizer.js

As said above we will use satellizer for our ionic app. It will take core of the auth code generation with facebook.

bower install satellizer --save

satellizer also requires cordova inapp browser plugin in order to work inside your phone.
So we will install it simply by typing cordova plugin add cordova-plugin-inappbrowser inside our cli.

You will also need cordova-plugin-whitelist which comes by default with ionic, but if for some reason your unable to communicate with the server from your phone, it may be the problem.

In this tutorial we will only use our app with ionic serve so you can skip the cordova plugins, but if you plan to take your app and deploy it for test in an actual phone make sure you installed both plugins.

Now open index.html file located inside the www folder. This file be the main entry point for our Ionic app.

Here we will add the satellizer plugin under cordova.js file.

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

    <script src="lib/satellizer/satellizer.js"></script>

Next, we must add satellizer as an app dependency. Open app.js located at www/js/app.js and add satellizer as an app dependency as follows:

angular.module('starter', ['ionic', 'satellizer', 'starter.controllers'])

Login Page

We will use ionic's default login page available to us from the starter pack. So open the login.html file located in templates folder.

<ion-modal-view>
  <ion-header-bar>
    <h1 class="title">Login</h1>
    <div class="buttons">
      <button class="button button-clear" ng-click="closeLogin()">Close</button>
    </div>
  </ion-header-bar>
  <ion-content>
    <form>
      <div class="list">
        <label class="item">
          <button class="button button-block button-positive" ng-click="authenticate('facebook')">Facebook Auth</button>
        </label>
      </div>
    </form>
  </ion-content>
</ion-modal-view>

We removed the username/password fields and removed the form submit handler. Instead we attached the Facebook Auth button an ng-click attribute that will call in turn to our login controller.

UserService

Next we will create a centralised UserService that will perform the authentication and manage login state.

Create a new file inside js folder and name it user.service.js

(function(){
  "use strict";

  angular
    .module('starter')
    .factory('UserService', UserService);

  function UserService($rootScope, $auth) {
    var userData = $auth.getPayload();

    return {
      isAuthenticated: function(){
        return $auth.isAuthenticated();
      },
      authenticate: function(provider) {
        $auth
          .authenticate(provider)
          .then(this.successAuth)
          .catch(this.failedAuth);
      },
      logOut: function() {
        $auth.logout();
        userData = undefined;

        $rootScope.$emit('userLoggedOut');
      },
      getUser: function(){
        return userData;
      },
      successAuth: function() {
        userData = $auth.getPayload();

        $rootScope.$emit('userLoggedIn', {data: userData});
      },
      failedAuth: function() {
        userData = undefined;
        $rootScope.$emit('userFailedLogin');
      }
    }
  }
})();

This service will hold the user data retrieved from the server and will perform the authentication process. Add user.service.js to your index.html file:

<script src="js/app.js"></script>
<script src="js/user.service.js"></script>
<script src="js/controllers.js"></script>

After creating our service we can inject it to our controller AppCtrl located inside controllers.js file.

We will then create a new method to match the ng-click we provided our view earlier:

$scope.authenticate = function(provider) {
    UserService.authenticate(provider);
}

Then we will use an event handler for an event fired from UserService to be informed for when the user is logged or not.

The entire AppCtrl will look like this:

.controller('AppCtrl', function($rootScope, $scope, $ionicModal, UserService) {

  // Create the login modal that we will use later
  $ionicModal.fromTemplateUrl('templates/login.html', {
    scope: $scope
  }).then(function(modal) {
    $scope.modal = modal;
  });

  // Triggered in the login modal to close it
  $scope.closeLogin = function() {
    $scope.modal.hide();
  };

  // Open the login modal
  $scope.login = function() {
    $scope.modal.show();
  };

  $scope.authenticate = function(provider) {
    UserService.authenticate(provider);
  };

  $rootScope.$on('userLoggedIn', function(data){
    // here we will recieve the logged in user
    console.log(data);
    $scope.closeLogin();
  });

  // will fire in case authentication failed
  $rootScope.$on('userFailedLogin', function(){

  });
})

And lastly we will need to configure satellizer provider inside app.js file. Add $authProvider as the config dependency so our config will look like:

.config(function($stateProvider, $urlRouterProvider, $authProvider) {
    var commonConfig = {
            popupOptions: {
                location: 'no',
                toolbar: 'yes',
                width: window.screen.width,
                height: window.screen.height
            }
        };

        if (ionic.Platform.isIOS() || ionic.Platform.isAndroid()) {
            commonConfig.redirectUri = 'http://localhost:8100/';
        }
        $authProvider.facebook(angular.extend({}, commonConfig, {
            clientId: 'your client id from facebook console',
            url: 'http://localhost:3000/auth/facebook'
        }));
})

Here we are specifying the redirect popup settings, adding our redirectUri (as configured in facebook console, it must match the server port and address running ionic).

Finally we are extending our commonConfig with facebook clientId and the url of our server endpoint that will catch the Authorization code passed to it automatically by satellizer.

run ionic serve and navigate to the login modal from the left menu, when clicking on the auth with facebook button you will be redirected to the facebook prompt page, add your credentials(The user with login in must be authorized inside the facebook app settings).

You will then be redirect back to the app and the userLoggedIn event will fire, you can have the user information extracted within the controller easily with:

 $rootScope.$on('userLoggedIn', function(){
    $scope.userData = UserService.getUser();
  });

You then can assign the data to your scope, or use it inside your UI. Satellizer will automatically send the JWT token assigned to the "authorization" Header on every request made to the server.

You can follow the getting started tutorial from ionic and build the app to your ios\android platform for testing. When you do so, remember to change the url in satellizer's configuration to match your server address.