The job of an authentication 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

NOTE: This is assuming you want your guard to be in that path, you can create it wherever you want.

Once it’s created, it’ll look 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 { getAuth, onAuthStateChanged } from "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) => {
      const auth = getAuth();

      onAuthStateChanged(auth, (user) => {
        if (user) {
          resolve(true);
        } else {
          console.log('User is not logged in');
          this.router.navigate(['/login']);
          resolve(false);
        }
      });
    });
  }
}

We’re importing the 2 things we need from Firebase authentication:

import { getAuth, onAuthStateChanged } from 'firebase/auth';

The getAuth() method helps us get the current authentication instance, and the onAuthStateChanged() method checks if there’s a user logged in.

If there is a user, 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: '',
  loadChildren: () => import('./pages/home/home.module').then(m => m.HomePageModule),
  canActivate: [AuthGuard],
},
{
  path: 'home',
  redirectTo: '',
  pathMatch: 'full',
},
{
  path: 'login',
  loadChildren: () => import('./pages/login/login.module').then(m => m.LoginPageModule)
},

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.