Learn how to use Angular Guards to protect your pages in Ionic apps

First Published: 25 July 2018
Updated on: 24 September 2018

To explain the concept of an authentication guard, we need to first get a bit of context about how the navigation works on Ionic Framework v4+.

Ionic uses Angular Router for navigation, which moves the user through the application using URLs, if you go ahead and open the src/app folder, you’ll find the file app-routing.module.ts, open it and you’ll see the CLI created a route for every page you generated.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: './pages/home/home.module#HomePageModule' },
  {
    path: 'event-create',
    loadChildren:
      './pages/event-create/event-create.module#EventCreatePageModule'
  },
  ...
  ...
  ...
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Those routes use lazy loading, meaning that when the app starts it’s not going to load them all, instead, it’s going to load them on a “need-this-now” case.

That’s why you don’t see the URLs pointing to a specific component, but instead to a module.

The URLs work like on a regular website, if you go to yourdomain.com/home it will take you to the home page.

Authentication Guards

Now that we have our URLs ready, we want to create an authentication guard, the job of this guard is to fetch for the user’s authentication state and return true/false depending on whether or not there’s a logged in user.

If it returns true the user will be able to navigate to that page if it returns false we should redirect the user somewhere else.

Open your terminal and use the CLI to create the guard:

ionic generate guard services/user/auth

Since the guard is a service related to authentication we’re creating it in the /user folder. If you open it you’ll find something like this:

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

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor() {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    return true;
  }
}

We’re going to use the onAuthStateChanged() method, it connects to Firebase Authentication and listens to changes in the user state to respond to them. You can read more about it in Firebase Official Documentation.

However, the TL;DR is that it adds an observer for auth state changes, meaning that whenever an authentication change happens, it will trigger the observer and the function inside it will run again.

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router,
} from '@angular/router';
import { Observable } from 'rxjs';
import * as firebase from 'firebase/app';
import 'firebase/auth';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    return new Promise((resolve, reject) => {
      firebase.auth().onAuthStateChanged((user: firebase.User) => {
        if (user) {
          resolve(true);
        } else {
          console.log('User is not logged in');
          this.router.navigate(['/login']);
          resolve(false);
        }
      });
    });
  }
}

We’re importing the core of Firebase functionality and then adding the auth functionality to the namespace:

import firebase from 'firebase/app';
import 'firebase/auth';

After that, we’re using the onAuthStateChanged() function to see if there’s a user, if there is, we resolve the promise with true, if there isn’t, we send back false and use the router to redirect the user to the login page.

Now that our Guard is created, we can call it from inside the app-routing.module.ts file:

import { AuthGuard } from './services/user/auth.guard';

{
  path: 'home',
  loadChildren: './pages/home/home.module#HomePageModule',
  canActivate: [AuthGuard],
},
{
  path: 'event-create',
  loadChildren:
    './pages/event-create/event-create.module#EventCreatePageModule',
  canActivate: [AuthGuard],
},
{
  path: 'event-detail/:id',
  loadChildren:
    './pages/event-detail/event-detail.module#EventDetailPageModule',
  canActivate: [AuthGuard],
}

All you need to do is add the canActivate property with the value of the AuthGuard (or any other guard you create) to the routes you want to be protected.

If you want to take a deeper dive on Ionic + Firestore you should go through my FREE course: Build your first Firestore powered Ionic app.