Loading...
Authentication as a Service

Authentication as a Service – Creating an application using Auth0 and Ionic 2

Hello guys,

In this second article of the “Authentication as a Service” series, we will demonstrate the process of creating a new application in Auth0.

After authenticate in Auth0, you will see a pretty extensive menu with various features.

To begin a new application, look for the “Dashboard” option and select the “+ New Application” option.

In this example we will create an application called “Todo App” to add tasks to be done, a classic example 🙂

The framework that we will use for this project is the Ionic 2 beta, a framework for the development of hybrid applications based on Angular 2.

First, if you do not have the Ionic beta 2 installed, use the following command to perform the installation:

1
$ npm install -g ionic@beta

Now, we will create a new Ionic project using the following command:

1
$ ionic start auth0-ionic --v2 -ts

After creation, we can enter the directory:

1
$ cd auth0-ionic

And to test your creation, you can run the following command:

1
2
3
4
5
6
7
8
$ ionic serve
√ Running dev server:  http://192.168.1.13:8100
Ionic server commands, enter:
  restart or r to restart the client app from the root
  goto or g and a url to have the app navigate to the given url
  consolelogs or c to enable/disable console log output
  serverlogs or s to enable/disable server log output
  quit or q to shutdown the server and exit

And as a result:

Ok, at this step, we have already started our application on the dashboard of Auth0, and started our Ionic project.

The next step is to add support for angular2-jwt library in our project. We will use it in order to provide support adding a JWT (JSON Web Token) in our requests.

1
$ npm install angular2-jwt --save

The next step is to add the dependencies, provide, Http, AuthHttp e AuthConfig in our application. for that, add in the app/app.ts the following lines:

1
2
3
4
...
import {provide} from 'angular2/core';
import {Http} from 'angular2/http';
import {AuthHttp, AuthConfig} from 'angular2-jwt';

And after this addition, we will set up our provider in our constructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@App({
  template: '<ion-nav [root]="rootPage"></ion-nav>',
  config: {},
  providers: [
    provide(AuthHttp, {
      useFactory: (http) => {
        return new AuthHttp(new AuthConfig(), http);
      },
      deps: [Http]
    }),
  ]
})
export class TodoApp {
...
}

After this step, we will add a class containing the information necessary for our authentication service, for this, we create a file called config-auth0.ts inside our directory app/.

In this file, we will add our application security credentials, to obtain them, just enter the Settings option from our application, as below:

After we get the credentials, we can add them in our config-auth0 file:

1
2
3
4
export class ConfigAuth0 {
    static CLIENT_ID:string = "CLIENT_ID";
    static AUTH0_DOMAIN:string = "DOMAIN_NAME";
}

After we configure our class that will store our credentials, we can start setting our authentication service, which will be responsible for the processes of login, logout and check if the user is or is not logged into the application.

And will also be responsible for the Lock setting. Lock is another service provided by Auth0. Its use allows us to add in our application, a login screen that will respond according to the settings added in our application, such as single sign on, Social Logins, etc.

For this, we will add to our project a class called AuthService, located at: app/services/auth-service.ts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import {Storage, LocalStorage} from 'ionic-angular';
import {AuthHttp, JwtHelper, tokenNotExpired} from 'angular2-jwt';
import {Injectable, NgZone} from 'angular2/core';
import {Observable} from 'rxjs/Rx';
import {ConfigAuth0} from '../config-auth0.js';
 
declare var Auth0Lock: any;
 
@Injectable()
export class AuthService {
 
    jwtHelper: JwtHelper = new JwtHelper();
    lock = new Auth0Lock(ConfigAuth0.CLIENT_ID, ConfigAuth0.AUTH0_DOMAIN);
    local: Storage = new Storage(LocalStorage);
    refreshSubscription: any;
    user: Object;
    zoneImpl: NgZone;
 
    constructor(private authHttp: AuthHttp, zone: NgZone) {
        this.zoneImpl = zone;
        this.local.get('profile').then(profile => {
            this.user = JSON.parse(profile);
        }).catch(error => {
            console.log(error);
        });
    }
 
    public authenticated() {
        return tokenNotExpired();
    }
 
    public login() {
        this.lock.show({
            authParams: {
                scope: 'openid offline_access',
                device: 'Mobile device'
            }
        }, (err, profile, token, accessToken, state, refreshToken) => {
            if (err) {
                alert(err);
            }
            this.local.set('profile', JSON.stringify(profile));
            this.local.set('id_token', token);
            this.local.set('refresh_token', refreshToken);
            this.zoneImpl.run(() => this.user = profile);
        });    
    }
 
    public logout() {
        this.local.remove('profile');
        this.local.remove('id_token');
        this.local.remove('refresh_token');
        this.zoneImpl.run(() => this.user = null);
    }
 
}

After we configure our authentication service and Lock, we will add the Lock in our index.html so that our application starts with the login screen. For this, we will modify the file www/index.html adding content just above the :

1
2
3
4
5
<head>
....
<!-- Auth0 Lock script -->
<script src="http://cdn.auth0.com/js/lock-9.1.min.js"></script>
</head>

Now, we will add in our main class (app.ts) the AuthService provider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
import {provide} from 'angular2/core';
import {Http} from 'angular2/http';
import {AuthHttp, AuthConfig} from 'angular2-jwt';
import {AuthService} from './services/authservice';
 
@App({
  template: '<ion-nav [root]="rootPage"></ion-nav>',
  config: {},
  providers: [
    provide(AuthHttp, {
      useFactory: (http) => {
        return new AuthHttp(new AuthConfig(), http);
      },
      deps: [Http]
    }), AuthService]
})
export class TodoApp {
....
}

Okay, after this step, we will activate the support to Social Login. For this, we will select the optionSocial at Connections menu, and enable the Twitter option.

After that, in a new window in your browser open the Twitter – Application Management and select the “Create New App” option.

After opening the window and fill in the basic information, we should be alert to the Callback URL. This field should be filled as follows: http://[YOUR-LOGIN-AUTH0].auth0.com/login/callback

After confirming the information, we will get the information API Key and API Secret at Access Keys and Tokens:

Getting the information, we will add them in the Twitter setting at Auth0 control panel:

Now, we will add a page that will show the profile of our user logged in, and if you have not been authenticated, it will prompt the user to log in the application for this, we will add a directory called perfil inside the pages directory, and add two files: perfil.html and perfil.ts. We will also remove the folders page1, page2, page3 and tabs in the pages directory.

In the perfil.ts file, we will add the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
import {Page} from 'ionic-angular';
import {AuthService} from '../../services/authservice';
 
@Page({
  templateUrl: 'build/pages/perfil/perfil.html',
})
export class PerfilPage {
 
    constructor(private auth: AuthService) {
 
    }
 
}

In the perfil.html file, we will add the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<ion-navbar *navbar>
    <ion-title>Profile</ion-title>
</ion-navbar>
 
<ion-content padding *ngIf="!auth.authenticated()">
    <button block (click)="auth.login()">Login</button>
</ion-content>
 
<ion-content padding *ngIf="auth.authenticated()">  
    <ion-card>
        <ion-item *ngIf="auth.user">
            <ion-avatar item-left><img src="{{ auth.user.picture }}"></ion-avatar>
            <h2>{{ auth.user.nickname }}</h2>
            <p>{{ auth.user.email }}</p>
        </ion-item>
    </ion-card>
    <button block (click)="auth.logout()">Logout</button>
</ion-content>

We will also removing the following content at the app/theme/app.core.scss file.

1
2
3
@import "../pages/page1/page1";
@import "../pages/page2/page2";
@import "../pages/page3/page3";

Finally, we will change the app/app.ts file in the following lines:

Line 3

1
import {PerfilPage} from './pages/perfil/perfil';

Line 21

1
rootPage: any = PerfilPage;

At this stage via command line, we will add the cordova inappbrowser plugin, as follows:

1
$ ionic plugin add cordova-plugin-inappbrowser

Now, we can generate a version of our application to test it on our device, to do this, we run the following statement for Android:

1
2
3
4
5
6
7
8
9
10
11
$ ionic build android
Running 'build:before' gulp task before build
[20:50:01] Starting 'clean'...
[20:50:01] Finished 'clean' after 23 ms
[20:50:01] Starting 'build'...
[20:50:01] Starting 'sass'...
....
BUILD SUCCESSFUL
 
Total time: 3.956 secs
Built the following apk(s): /Users/horochovec/Projects/horochovec/auth0-ionic/platforms/android/build/outputs/apk/android-debug.apk

After this step, we can start to install the APK on our device for testing.

Login screen:

Authorization screen:

And after we make our login, we list our first user added to our account in Auth0:

As a last step of our application, we will add the refresh-token support in our AuthService as the following changes in the app/services/authservice.ts.

Add the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    public scheduleRefresh() {
      let source = this.authHttp.tokenStream.flatMap(
        token => {
          let jwtIat = this.jwtHelper.decodeToken(token).iat;
          let jwtExp = this.jwtHelper.decodeToken(token).exp;
          let iat = new Date(0);
          let exp = new Date(0);
 
          let delay = (exp.setUTCSeconds(jwtExp) - iat.setUTCSeconds(jwtIat));
 
          return Observable.interval(delay);
        });
      this.refreshSubscription = source.subscribe(() => {
      this.getNewJwt();
    });
  }
 
  public startupTokenRefresh() {
    if (this.authenticated()) {
      let source = this.authHttp.tokenStream.flatMap(
        token => {
          let now: number = new Date().valueOf();
          let jwtExp: number = this.jwtHelper.decodeToken(token).exp;
          let exp: Date = new Date(0);
          exp.setUTCSeconds(jwtExp);
          let delay: number = exp.valueOf() - now;
          return Observable.timer(delay);
        });
        source.subscribe(() => {
          this.getNewJwt();
          this.scheduleRefresh();
        });
    }
  }
 
  public unscheduleRefresh() {
    if (this.refreshSubscription) {
      this.refreshSubscription.unsubscribe();
    }
  }
 
  public getNewJwt() {
    this.local.get('refresh_token').then(token => {
      this.lock.getClient().refreshToken(token, (err, delegationRequest) => {
        if (err) {
          alert(err);
        }
        this.local.set('id_token', delegationRequest.id_token);
      });
    }).catch(error => {
      console.log(error);
    });
  }

And correct login() and logout() methods as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    public login() {
        this.lock.show({
            authParams: {
                scope: 'openid offline_access',
                device: 'Mobile device'
            }
        }, (err, profile, token, accessToken, state, refreshToken) => {
            if (err) {
                alert(err);
            }
            this.local.set('profile', JSON.stringify(profile));
            this.local.set('id_token', token);
            this.local.set('refresh_token', refreshToken);
            this.zoneImpl.run(() => this.user = profile);
            this.scheduleRefresh();
        });    
    }
 
    public logout() {
        this.local.remove('profile');
        this.local.remove('id_token');
        this.local.remove('refresh_token');
        this.zoneImpl.run(() => this.user = null);
        this.unscheduleRefresh();
    }

Finally, in our main class app/app.ts, to add the request to the refreshtoken at the time the application is available to the user:

1
2
3
4
5
6
7
8
9
10
export class TodoApp {
  rootPage: any = PerfilPage;
 
  constructor(platform: Platform, private authHttp: AuthHttp, private auth: AuthService) {
    platform.ready().then(() => {
      StatusBar.styleDefault();
      this.auth.startupTokenRefresh();
    });
  }
}

All the code in this example can be seen at Github.

And the Android app can be Download at Play Store.

Leave a Reply

Your email address will not be published. Required fields are marked *