Ionic [2|3] | Handling a Simple User Authentication

Written by on October 13, 2017

Ionic [2|3] | Handling a Simple User Authentication

In my previous Ionic [2|3] based article: Ionic 2 | Successful OAuth Social Login with Firebase, I demonstrated how to integrate a Firebase OAuth social login support with Ionic. Today I will show how to make a simple application authorization.

Note: If this tutorial was helpful, you need further clarification, something is not working or you have a request for another Ionic post? Furthermore, leave me a comment below if you don't like something about this blog, if something is bugging you, don't like how I'm doing stuff here. Feel free to comment below, subscribe to my blog, mail me to dragan.gaic@gmail.com. Thanks and have a nice day!

Preparations

Before we do anything, make sure you have everything set up for Ionic [2|3] development. One last thing, as this framework is now known only as Ionic, further down the line I will refer to it as such; I will not use any version numbers. I will simply refer to it as Ionic.

To continue working on this tutorial you should have these:

  • Android Environment (or iOS if you’re working on a MacOS)
  • nodeJS
  • Ionic
  • Cordova

If you don’t have a prior Ionic installation find more information here: Ionic [2|3] | Installation Guide.

1. Update Ionic CLI

If possible you should have the latest nodeJS version, without it, you’ll not be able to appropriately install/update Cordova and Ionic. Worst case scenario, use any recent version possible.

On the other hand, If you have already worked with Ionic/Cordova, make sure they’re up to date; older versions may not work with this tutorial:

npm install -g ionic cordova

or to do a simple update:

npm update -g ionic cordova

2. Create A New Project

ionic start IonicSimpleAuthorizationExample blank
cd IonicSimpleAuthorizationExample

If you prefer a working example you will find it at the end of this article; if not, keep reading.

Warning: As some of you don't have a prior Ionic CLI experience, from this point and on, every time I tell you to execute something, do that inside an example project folder.

3. Add Required Platform

Add Android platform:

ionic cordova platform add android

macOS users can also add the iOS platform:

ionic cordova platform add ios

Example

First take a look at this embedded example:

Ionic 2 Authorization Example

Source Code and GitHub repo link can be found on the next page.

Source Walkthrough

This tutorial requires at least two pages. The first page will hold authorization form. The second one is just a plain home page where the application will end up after successful authorization.

As there’s already one existing page (component) HomePage, we will need to add one more (authorization page):

ionic generate page auth

This will generate a new folder called auth (inside pages folder) including our new AuthPage page. This process is done automatically, a AuthPage name was formed using page name we provided with ionic generate.

You can also do this manually but this way is much faster and error free.

Before we can proceed any further, include this new page into app.module.ts file (as currently this is not done automatically):

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AuthPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AuthPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

Let us proceed to app.component.ts file.

This is the point where we need to decide which page is going to be shown first. If a user has previously used this application, there’s a good chance authorization data is already stored in local storage. And if provided authorization data is correct (we’re assuming authorization process was successful) application will route us to HomePage. On the other hand, if authorization process was unsuccessful or if authorization data was not stored in local storage, application will navigate to AuthPage:

  checkPreviousAuthorization(): void { 
    if((window.localStorage.getItem('username') === "undefined" || window.localStorage.getItem('username') === null) && 
       (window.localStorage.getItem('password') === "undefined" || window.localStorage.getItem('password') === null)) {
      this.rootPage = AuthPage;
    } else {
      this.rootPage = HomePage;
    }
  }

Also, don’t forget to include AuthPage, as HomePage is the only one included by default:

import { AuthPage } from '../pages/auth/auth';

At this point we have two pages and depending on the localStorage status application will display either AuthPage or HomePage.

I will borrow authorization page from my previous article here: Ionic [2|3] | How To Create And Validate Forms. As form building is also covered in the previously mentioned article I will not describe it here. We will just fast forward to authorization function.

This example will work on the assumption that authorization is always successful; this way I don’t need to prepare a server-side code.

 
onSubmit(value: any): void { 
    if(this.authForm.valid) {
        window.localStorage.setItem('username', value.username);
        window.localStorage.setItem('password', value.password);

        this.nav.push(HomePage);
    }
} 

So far so good. On the next page, we will show a welcome message:

constructor(public nav: NavController) {
    this.nav = nav;
    this.username = window.localStorage.getItem('username');
}
<h2>Welcome {{username}}</h2>

Unfortunately, there’s one small problem. Because we made a transition between AuthPage and HomePage, our application will show a back button. As we are using an authorization component, this is something we want to avoid. There’s only one case where we want to allow back functionality, and that’s when a user clicks on the Logout button.

To hide back button, add hideBackButton keyword to HomePage NavBar component:

<ion-header>
  <ion-navbar hideBackButton>
    <ion-title>Home Page</ion-title>
  </ion-navbar>
</ion-header>

If we close and reopen this application, because there are stored user data in local storage, app.component.ts will navigate us to the HomePage.

What will happen, if we click on a Logout button, and application navigates us back to the AuthPage again? We will indeed witness a transition, but HomePage will stay in a navigation stack (navigation history). For example, if we click on a back button (Android, this will thankfully not work on iOS) we will navigate back to the HomePage. Again, this is something we want to avoid.

During logout action, we will first remove username and password stored in local storage. Next, we will set FormPage as a root page and pop directly to it, thus cleaning existing navigation history:

logout(): void { 
    window.localStorage.removeItem('username');
    window.localStorage.removeItem('password');

    this.nav.setRoot(AuthPage);
    this.nav.popToRoot();   
}  

If we restart our app again, the root component will navigate us to the AuthPage.

Source Code

In case GitHub repo is not available, and you can only use code provided below, this is what project folder looks like (image was taken from the Sublime text editor):

Ionic 2 Simple Authorization Project Structure

I’ll show you only relevant files; everything else is available in provided GitHub repo.

app.component.ts
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';

@Component({
  templateUrl: 'app.html'
})

export class MyApp {
  rootPage:any;

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() => {
      statusBar.styleDefault();
      splashScreen.hide();
      this.checkPreviousAuthorization();
    });
  }

  checkPreviousAuthorization(): void { 
    if((window.localStorage.getItem('username') === "undefined" || window.localStorage.getItem('username') === null) && 
       (window.localStorage.getItem('password') === "undefined" || window.localStorage.getItem('password') === null)) {
      this.rootPage = AuthPage;
    } else {
      this.rootPage = HomePage;
    }
  }  
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AuthPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AuthPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}
auth.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

import { HomePage } from '../home/home';

@IonicPage()
@Component({
  selector: 'page-auth',
  templateUrl: 'auth.html',
})

export class AuthPage {

	authForm: FormGroup;

	constructor(public nav: NavController, public navParams: NavParams, public formBuilder: FormBuilder) {

		this.nav = nav;

	    this.authForm = formBuilder.group({
	        username: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*'), Validators.minLength(8), Validators.maxLength(30)])],
	        password: ['', Validators.compose([Validators.required, Validators.minLength(8)])]
	    });
	}

	onSubmit(value: any): void { 
	    if(this.authForm.valid) {
			window.localStorage.setItem('username', value.username);
			window.localStorage.setItem('password', value.password);

			this.nav.push(HomePage);
	    }
	}   
}
auth.html
<ion-header>
  <ion-navbar>
    <ion-title>Authorization Page</ion-title>
  </ion-navbar>
</ion-header>


<ion-content padding>
	<form [formGroup]="authForm" (ngSubmit)="onSubmit(authForm.value)">
        <ion-item>
            <ion-label floating>Username</ion-label>
            <ion-input formControlName="username" type="text"></ion-input>
        </ion-item>

        <ion-item *ngIf="authForm.controls.username.hasError('required') && authForm.controls.username.touched">
            <p>Sorry, field username is required!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.username.hasError('pattern') && authForm.controls.username.touched">
            <p>Sorry, only small and capital letters are allowed!</p>
        </ion-item>        
        <ion-item *ngIf="authForm.controls.username.hasError('minlength') && authForm.controls.username.touched">
            <p>Sorry, minimum username length is 8!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.username.hasError('maxlength') && authForm.controls.username.touched">
            <p>Sorry, maximum username length is 30!</p>
        </ion-item>

        <ion-item>
            <ion-label floating>Password</ion-label>
            <ion-input formControlName="password" type="password"></ion-input>
        </ion-item>
        <ion-item *ngIf="authForm.controls.password.hasError('required') && authForm.controls.password.touched">
            <p>Sorry, field password is required!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.password.hasError('minlength') && authForm.controls.password.touched">
            <p>Sorry, minimum password length is 8!</p>
        </ion-item>                

		<button ion-button full color="primary" [disabled]="!authForm.valid" style="margin-top: 20px;" type="submit">Authorize</button>        
	</form>
</ion-content>
home.ts
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

import { AuthPage } from '../auth/auth';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

	username: string;

	constructor(public nav: NavController) {
		this.nav = nav;
		this.username = window.localStorage.getItem('username');
	}

	logout(): void {
		window.localStorage.removeItem('username');
		window.localStorage.removeItem('password');

		this.nav.setRoot(AuthPage);
		this.nav.popToRoot();   
	} 	
}
home.html
<ion-header>
  <ion-navbar hideBackButton>
    <ion-title>Home Page</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <h2>Welcome {{username}}</h2>
  <button ion-button full color="primary" style="margin-top: 20px;" (click)="logout()">Logout</button>
</ion-content>

Deployment

Next step, build our application:

ionic build android

Be careful here, this step may break if you’re behind a firewall. The first execution will take a long time, so be patient.

Finally:

ionic run android -l -c -s

or, as this example is not bound to any platform, you can run it in your browser:

ionic serve -l -c -s

Download The Code

Working GitHub repo link can be found below:

GitHub
Categories

4 thoughts on “Ionic [2|3] | Handling a Simple User Authentication”

  1. Does window.localstorage something works in ionic 2 or have they replaced it with ionic native LocalStorage

  2. hello,
    without showing form page, its directly showing home page and when click on logout button that showing logout() is not a function in console.

    regards

Leave a Reply