Ionic 4 | Working Authentication Example (Angular)

Written by on July 8, 2019

Ionic 4 | Working Authentication Example (Angular)


The following is a custom example and tutorial on how to implement a working authentication example with Ionic 4.

As this is a complex tutorial I will change the usual format of my articles. Instead of following the process with small code snippets, I will combine the whole code examples with underlying code commentary, in the hope it will make it easier to understand. To make your and my life even easier, I will also reuse my previous tutorials, first and foremost Ionic 4 | How To Create And Validate Forms.

We will not use the real backend API as this is just an example application; we will mock it instead. To use actual backend API just comment down dummyBackendProvider in /src/app/app.module.ts file.

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 Thanks and have a nice day!


As in all my other tutorials on this topic, you need to have a working Ionic 4 workspace.

You will need these to finish your tutorial:

  • Android or iOS environment (optional as we can also run it in our browser)
  • NodeJS
  • Ionic
  • Cordova

If you neet to set-up a working Ionic 4 environment just follow this tutorial: Ionic [2|3] | Installation Guide.

1. Update Ionic CLI

Install or update your current Ionic version:

npm install -g ionic cordova

To update:

npm update -g ionic cordova

2. Create A New Project

ionic start IonicAuthenticationExample blank
cd IonicAuthenticationExample

You will find a working example at the end of this article, or keep reading if you are interested in how it works. Jump to the working example here.

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

If you want to use the browser:

ionic cordova platform add browser

Android platform:

ionic cordova platform add android

iOS platform:

ionic cordova platform add ios

Code Overview

Code Overview

The folder structure below presents our active project. By clicking on the blue link you will immediately jump on that specific file.

Folder Structure

Authentication Page

Info: I have borrowed the approach originaly done by Jason Watmore, it can be found here.

I have reused authentication page from my previous article on form validation topic (you will find the link in the opening words). As such, I will not go into great details with this part of the implementation. If you would like to know more just follow the above tutorial link.

We are using a very simple authentication page, two fields only, email and password. The only difference compared to my previous example is that on form submit we will trigger the actual login process.

The authentication process is handled in the authService.

To make this example as simple we are using a dummy backend provider that intercepts the HTTP requests and send back fake responses. More about it in the next chapter.

<ion-content padding="true">
    <form [formGroup]="authForm" (ngSubmit)="onSubmit(authForm.value)">
            <ion-label>Username (Email)</ion-label>
            <ion-input formControlName="email"></ion-input>
        <div *ngIf="submitted &&" class="invalid-feedback">
            <ion-item *ngIf="">
                <p style="color:red">Email is required!</p>
            <ion-item *ngIf="">
                <p style="color:red">Email must be a valid email address!</p>
            <ion-input formControlName="password" type="password"></ion-input>
        <div *ngIf="submitted && frm.password.errors" class="invalid-feedback">
            <ion-item *ngIf="frm.password.errors.required">
                <p style="color:red">Password is required!</p>
            <ion-item *ngIf="frm.password.errors.minlength">
                <p style="color:red">Password must be at least 6 characters!</p>
                    <ion-button expand="block" size="large" type="submit">Authenticate  <ion-spinner name="bubbles" *ngIf="loading"></ion-spinner></ion-button>    
                    <ion-button expand="block" size="large" type="reset" (click)="onReset()">Reset</ion-button> 

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { first } from 'rxjs/operators';
import { AlertController } from '@ionic/angular';

import { AuthService } from '../../services/auth.service';
  selector: 'app-auth',
  templateUrl: './',
  styleUrls: ['./'],
export class AuthPage implements OnInit {
    submitted = false;
    authForm: FormGroup;
    returnUrl: string;
    loading = false;
    constructor(private router: Router,
                private route: ActivatedRoute, 
    	        private formBuilder: FormBuilder,
    	        private authService: AuthService,
    	        private alertController: AlertController) { 
    ngOnInit() {
        this.authForm ={
            email: ['', [Validators.required,]],
            password: ['passw0rd', [Validators.required, Validators.minLength(6)]]
        }, {});

        this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';        
    onSubmit(value: any): void { 
        this.submitted = true;
        // Stop if the form validation has failed
        if (this.authForm.invalid) {
        this.loading = true;
        this.authService.login(, this.frm.password.value)
                data => {
                    this.loading = false;
                error => {
                    this.loading = false;

    onReset() {
        this.submitted = false;

    async presentAlert(msg) {
	const alert = await this.alertController.create({
	  header: 'Alert',
	  subHeader: '',
	  message: msg,
	  buttons: ['OK']

	await alert.present();
    get frm() { return this.authForm.controls; }    

Authentication Service

This is a simple service that handles login and logout tasks and provides the currently active user.

The login function sends a POST REST request to the preconfigured backend API. Environment constants are preconfigured in the environment.ts class, just outside of the app folder. If the authentication was successful, we will encode username and password with BASE64, store it in localstorage and use for authentication header.

The logout function will remove user locastorage object, remove it as current logged in user and send us back to the authentication page.

Finaly, currentUserValue function will return the current active user. We will use it to display loged in user in the Home page.


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';

import { User } from '../models/user';

  providedIn: 'root'
export class AuthService {
    private currentUserSubject: BehaviorSubject<User>;
    public currentUser: Observable<User>;

    constructor(private http: HttpClient, private router: Router) {
        this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')));
        this.currentUser = this.currentUserSubject.asObservable();

    public get currentUserValue(): User {
        return this.currentUserSubject.value;

    public login(email: string, password: string) {
        return<any>(`${environment.bffUrl}/users/authenticate`, { email, password })
            .pipe(map(user => {
                user.authdata = window.btoa(email + ':' + password);
                localStorage.setItem('currentUser', JSON.stringify(user));
                return user;

    public logout() {

User Model

The user model is a simple class describing the application User object.


export class User {
    id: number;
    email: string;
    password: string;
    firstName: string;
    lastName: string;
    authdata?: string;

Dummy Backend Provider

Dummy backend provider is used to fake backend real backend API. It is based on Angular HttpInterceptor, and as its name states it intercepts HTTP requests and send back fake responses.

To make it fell a bit more realistic, for each login related route, we are faking 0,5-sec response delay.

handleRoute function is used for actual interception. It will trigger only if requests match on of these two route patterns /users/authenticate and /users and their related request types (POST and GET).

Authentication process will trigger authenticate function. This is the location where actual faking is taking part. The list of users is already provided in the constant users, and we will match it against the incoming authentication form information. The function will return ok if the authentication result was successful, or error in the case of failure.

Another important part of this dummy interceptor is getUsers function. While it has no real purpose in this example it is put there to showcase how to handle specific tasks only if the user was successfully authenticated and the correct auth header is available. Of course, this is something you will need to implement on your backend.

To use the real backend provider just comment dummyBackendProvider in app.module.ts file.


import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';

import { User } from '../models/user';

const users: User[] = [{ id: 1, email: '', password: 'passw0rd', firstName: 'Dragan', lastName: 'Gaic' }];

export class DummyBackendInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const { url, method, headers, body } = request;

        return of(null)

        function handleRoute() {
            switch (true) {
                case url.endsWith('/users/authenticate') && method === 'POST':
                    return authenticate();
                case url.endsWith('/users') && method === 'GET':
                    return getUsers();
                    return next.handle(request);

        function authenticate() {
            const { email, password } = body;
            const user = users.find(x => === email && x.password === password);
            if (!user) return error('Email or password is incorrect');
            return ok({
                firstName: user.firstName,
                lastName: user.lastName

        function getUsers() {
            if (!isLoggedIn()) return unauthorized();
            return ok(users);

        function ok(body?) {
            return of(new HttpResponse({ status: 200, body }))

        function error(message) {
            return throwError({ error: { message } });

        function unauthorized() {
            return throwError({ status: 401, error: { message: 'Unauthorised' } });

        function isLoggedIn() {
            return headers.get('Authorization') === `Basic ${window.btoa('test:test')}`;

export let dummyBackendProvider = {
    useClass: DummyBackendInterceptor,
    multi: true

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';

import { dummyBackendProvider } from '../app/interceptors/dummy.backend.interceptor';

  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule],
  providers: [

    // Remove this line if you want to use the real BFF API
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  bootstrap: [AppComponent]
export class AppModule {}

Routing Interceptor

Routing interceptor is probably the most important part of this implementation. It will intercept application route changes to determine if the user is allowed to access sections of our application. For example, if the user
was not authenticated this interceptor will prevent it from accessing restricted routes.

To make it work we are using Angular CanActivate interface. Interceptor will be able to use canActivate() function to decide if a route can be activated or not. It will return true if we are allowed or false if we are not allowed.

To check if the user is allowed we are using authService object currentUserValue.


import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { AuthService } from '../services/auth.service';

@Injectable({ providedIn: 'root' })
export class RoutingInterceptior implements CanActivate {
        private router: Router,
        private authService: AuthService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUser = this.authService.currentUserValue;
        if (currentUser) {
            return true;

        this.router.navigate(['/auth'], { queryParams: { returnUrl: state.url } });
        return false;

Error and Authentication Interceptor

Above interceptors also serve a very important role. Error interceptor will intercept all backend error responses and logout user if the 401 Unauthorized response was detected. On the other hand, authentication interceptor will intercept all HTTP requests and create an authentication header if the active user was detected and if it holder BASE64 representation of username and password. Authentication Interceptor is not important in our example as it is not used, however, you will need this kind of logic to authorize the use of other backend API calls.


import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { AuthService } from '../services/auth.service';

export class ErrorInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(catchError(err => {
            if (err.status === 401) {

            const error = err.error.message || err.statusText;
            return throwError(error);

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

import { AuthService } from '../services/auth.service';

export class BasicAuthInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add authorization header with basic auth credentials if available
        const currentUser = this.authService.currentUserValue;
        if (currentUser && currentUser.authdata) {
            request = request.clone({
                setHeaders: { 
                    Authorization: `Basic ${currentUser.authdata}`

        return next.handle(request);

Home Page

Just like the authentication page, the home page is taken from my previous example. The only real difference is me showing a welcoming message to the authenticated user.

    <ion-title>Home Page</ion-title>

	<ion-card *ngIf="user">
			<ion-card-title >{{user.firstName}} {{user.lastName}}</ion-card-title>
	<ion-button expand="block" size="large" type="reset" (click)="logout()">Logout</ion-button> 

import { Component, OnInit } from '@angular/core';

import { AuthService } from '../../services/auth.service';
import { User } from '../../models/user';

  selector: 'app-home',
  templateUrl: './',
  styleUrls: ['./'],
export class HomePage implements OnInit {

    private user: User;

	constructor(private authService: AuthService) { }

	ngOnInit() {
		this.user = this.authService.currentUserValue;

    logout() {




As this is a browser-only example we can test it in our browser:

ionic serve

Of course, you can also test it using your Andriod smartphone:

ionic build android
ionic run android

Download The Code

Working example with step-by-step tutorial can be found here:



Here's a Way You Can Support Us And Benefit at the Same Time

If this article helped you here’s one thing you can do for me. While writing articles I’m using Notion tool for information gathering. It’s a modern alternative to Evernote. If you use Evernote or any similar application give also a chance to Notion. By signing up, you will get access to a very powerful tool, give me one more premium month, and in turn help me to write future articles.

More Ionic 4 Tutorials


Leave a Reply