Protecting Routes with Guards in Angular
The last feature we are going to add is to prevent unauthenticated users from getting to the home page, and to auto re-redirect logged in users to the home page. Firebase will remember users automatically, so if we are already logged in we should go directly to the home page.
IMPORTANT: The guards feature I am about to show you is for user experience only. These guards are implemented client side and would not stop a malicious user from getting to any page they like. If you need to restrict certain users from accessing certain things, this needs to happen on the backend - i.e. we should never load the data into the application in the first place. A route guard like the one we are implementing will not stop a malicious user from accessing a route. This is why we have things like our Firestore Security Rules. It doesn’t matter if a malicious user hacks their way past our route guard to gain access to the main /home
page without logging in, as no data will load in from Firebase if they haven’t authenticated. They will be on the /home
page, but it will be empty.
Create a Guard for the Login Page
Create a file at
src/app/shared/guards/login.guard.ts
and add the following:
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { NavController } from '@ionic/angular';
import { map, tap } from 'rxjs/operators';
import { AuthService } from '../data-access/auth.service';
@Injectable()
export class CanActivateLogin implements CanActivate {
constructor(
private authService: AuthService,
private navCtrl: NavController
) {}
canActivate() {
return this.authService.user$.pipe(
map((user) => (user ? false : true)),
tap((canActivate) => {
if (!canActivate) {
this.navCtrl.navigateForward('/home');
}
})
);
}
}
The general idea here is that we implement the CanActivate
interface. This means we need to supply a canActivate
method. We have this method return an observable that emits true
or false
, and that will determine if the user is allowed to access the route or not. In this case, if the user can not activate the /login
route it is because they are already logged in, and so we navigate them to the home
page.
To use this guard, we can supply it to our route.
Modify the
login
route inapp-routing.module.ts
to use the guard:
{
path: 'login',
canActivate: [CanActivateLogin],
loadChildren: () =>
import('./login/login.component').then((m) => m.LoginComponentModule),
},