Route Guards in Ionic & Angular

Prevent Access to Pages in Ionic with Angular Route Guards



·

In a previous tutorial, I covered how we would implement Angular routing in an Ionic 4.x application. When Ionic 4 is released, there will be more of a focus on using the baked in Angular routing, rather than Ionic’s own push/pop style routing (this style of navigation will still be available, though).

In this tutorial, we are going to cover how to use route guards with Angular routing to prevent access to certain routes if certain conditions have not been met. A common example of this is preventing access to certain pages if the user is not logged in, and that is what we will be focusing on.

In the past, you may have used the Ionic navigation guards like ionViewCanEnter to determine whether or not a user could navigate to a page. However, I don’t believe these functions will be available in Ionic 4.x (I’m not 100% certain on this). Instead, we should use Angular’s route guards to prevent access to certain pages in an Ionic/Angular application.

Angular Route Guards

The basic idea behind a route guard is that you attach a service which acts as the “route guard” to a particular route. That service has a canActivate method which will return either true or false depending on whether the user should be allowed to go to that route or not. If the canActivate method returns false, then the user will not be able to access the route.

Route guards make the process of protecting certain routes and redirecting the user quite simple, and in my opinion, more manageable than using navigation guards like ionViewCanEnter on individual components. The end result looks something like this:

const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', loadChildren: './pages/login/login.module#LoginModule' },
  { path: 'home', loadChildren: './pages/home/home.module#HomeModule', canActivate: [AuthGuardService] }
];

All we need to do is add one additional property to the route definitions to determine if the route can be activated. Since the routes in the example above are lazy loaded, we could also use canLoad instead of canActivate to entirely prevent the loading of the children for that route (rather than just preventing access).

It is important to note that most things we implement on the client side (i.e. not on a server) are more for user experience than security. Client-side code is modifiable by the user, so you should never use route guards to protect information that you don’t want the user to see (just as you shouldn’t solely use client-side code to validate/sanitise user entered data). Think of your route guards as a friendly security guard directing traffic – they can keep people away from where they are not supposed to be, and direct them to where they need to go, but anybody with malicious intent could easily run right by the guard. Anything in your application that needs to be kept secure should only be accessible through a server that your application communicates with.

Creating a Route Guard

Creating a route guard is as simple as creating a service that implements a canActivate method. All you need to do is have this method return true or false and you can do whatever you like to determine that value:

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

@Injectable()
export class AuthGuardService implements CanActivate {

    constructor(private router: Router) {

    }

    canActivate(route: ActivatedRouteSnapshot): boolean {

        console.log(route);

        let authInfo = {
            authenticated: false
        };

        if (!authInfo.authenticated) {
            this.router.navigate(['login']);
            return false;
        }

        return true;

    }

}

In this example, we have just set up a dummy object called authInfo that has an authenticated value of false. In a real-life situation, we would just pull this authenticaiton information from whatever is responsible for authenticating the user. We then check that value, and it the user is not authenticated we send them back to the login page and return false – otherwise, we just return true which will allow the navigation to proceed.

Although we are not using it, we have also injected ActivatedRouteSnapshot which will allow you to access details about the route that the user is navigating to. You may need details about the route, like the parameters that were supplied, in order to determine whether or not to allow a user to proceed.

Once you have created the auth guard service (and it does not need to be called that) you will also need to make sure to add it to the imports for your application just like any other service:

@NgModule({
  declarations: [
    AppComponent,
  ],
  entryComponents: [
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    IonicModule.forRoot()
  ],
  providers: [AuthGuardService],
  bootstrap: [AppComponent],
})
export class AppModule {}

Attach the Route Guard to your Routes

All that is left to do once you create the route guard is to import it into the file that contains your routes, and attach it to any routes you want to protect with it:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuardService } from './services/auth-guard.service';

const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', loadChildren: './pages/login/login.module#LoginModule' },
  { path: 'home', loadChildren: './pages/home/home.module#HomeModule', canActivate: [AuthGuardService] }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {

}

You can use multiple different route guards if you like, and you can attach the same route guard to multiple different routes.

Summary

The approach that Angular routing uses for route/navigation guards is quite similar in the end to the way you would have done it with ionViewCanEnter – ultimately, it is just a function returning true or false. However, I think the benefit of this approach is that it is a little more organised and it is easier to apply guards to multiple routes.