Web Development

Creating a Secure Role-Based App Using Angular Route Guard

April 28th, 2020 | By Jay Raj | 6 min read

Learn how to create a role-based app using Angular Route Guard. The source code for this tutorial is available on GitHub.

While creating a web app, setting up permissions based on roles is a common scenario. Some users may have permission to access certain pages or sections of the pages, depending on their user role.

Setting Up the App

Let's start by setting up the Angular app. Assuming you already have the Angular CLI installed, create a new Angular application.

ng new angular-role-app


The above command creates the basic boilerplate code for the Angular application. Navigate to the Angular project folder and start the application.

cd angular-role-app
npm start


You will have the Angular app running at localhost:4200.

Creating Angular Modules

Here are the three sections of our Angular application and their permissions:

  • Administration section: accessible only by a superuser

  • Management section: accessible only by a manager

  • General section: accessible by any user


Let's create a module for each of the above sections. Create the Admin module by using the following Angular CLI command:

ng g module Administration --routing


Add two components to the administration module: admin and adminHome.

ng g component administration/admin
ng g component administration/adminHome


Let's also define the routing for the administration module as shown:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AdminComponent } from './admin/admin.component';
import { AdminHomeComponent } from './admin-home/admin-home.component';

const routes: Routes = [
  { path: '', children :[
    { path : 'adminHome', component : AdminHomeComponent },
    { path : 'admin', component : AdminComponent },
    { path : '', redirectTo : 'admin', pathMatch : 'full' }
  ] }
];

@NgModule({
  declarations: [],
  imports: [RouterModule.forChild(routes)],
  exports : [RouterModule]
})
export class AdminRoutingModule { }


Similarly, create the management and general section modules using the command below. (The --routing option creates the routing file for each consecutive module.)

ng g module general --routing
ng g module management --routing


Create a dashboard component inside the general module.

ng g component general/dashboard


Define the routing for the general module as shown:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';

const routes: Routes = [
  { path: '', component: DashboardComponent }
];

@NgModule({
  declarations: [],
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})

export class GeneralRoutingModule { }


Create a component called management inside the management module.

ng g component management/management


Define the routing file for the management module as below:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ManagementComponent } from './management/management.component';

const routes: Routes = [
  { path: '', component: ManagementComponent }
];

@NgModule({
  declarations: [],
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})

export class ManagementRoutingModule { }


To enable the user to log in, create a Login component.

ng g component login


Now that you have the required modules and components ready, also define the routing module for the Angular application in the app-routing.module.ts file:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { 
    path: 'admin', 
    loadChildren: () => import('./administration/administration.module').then(m => m.AdministrationModule) 
  },
  { 
    path: 'general', 
    loadChildren: () => import('./general/general.module').then(m => m.GeneralModule) 
  },
  { 
    path: 'manage', 
    loadChildren: () => import('./management/management.module').then(m => m.ManagementModule) 
  },
  { path: '', redirectTo : 'login', pathMatch:'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }


As seen in the above routing module routes, LoginComponent is the default component.

Now, let's implement the logic for the Login component to secure our Angular application routes.

Implementing the Login Component

Start by implementing the Login component, which will authenticate the user.

For the sake of this tutorial, you'll be hard-coding the authentication process inside the Login component. This login process is for demonstration purposes only and should not be used in production apps.

You'll be using Bootstrap for styling the HTML pages. Install it on the Angular app using the following command:

npm install bootstrap jquery


Once done with the dependency installation, add the following style and script to the angular.json file:

"styles": [
	"src/styles.css",
	"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
	"node_modules/jquery/dist/jquery.min.js",
	"node_modules/bootstrap/dist/js/bootstrap.min.js"
]


Also, add the following HTML code to the login.component.html file:

<form class="form-signin">
    <img class="mb-4" src="/docs/4.4/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
    <label for="inputUsername" class="sr-only">Username</label>
    <input type="text" id="inputUsername" name="username" [(ngModel)]="username" class="form-control" placeholder="Username" required autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" id="inputPassword" name="password" [(ngModel)]="password" class="form-control" placeholder="Password" required>
   
    <button class="btn btn-lg btn-primary btn-block" (click)="handleLoginClick()" type="button">Sign in</button>
  </form>


And add the following code to the login.component.ts file:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  username;
  password;

  constructor(private http : HttpClient, private router : Router) { }

  ngOnInit(): void {
    
  }

  handleLoginClick(){
    if(this.username && this.password){
      this.authenticateUser(this.username);
    } else {
      alert('enter username and password');
    }
   
  }

  authenticateUser(userName){
    if(userName == "admin"){
      this.router.navigate(['/admin']);
    } else if(userName == "manager"){ 
      this.router.navigate(['/manage']);
    } else if(userName == "general"){
      this.router.navigate(['/general'])
    }
  }

}


Note: Authenticating using a username as shown above is not a secure way of doing so. This is for demo purposes only.

As seen in the authenticateUser method in login.component.ts, if the user role is admin, manager, or general user, he'll be redirected to the specific module.

Save the above changes and run the application. Try signing in as admin, and you'll be redirected to the admin module.

Securing Routes Using Angular Route Guard

There is an issue with the above implementation. The routes are not secure. If you log in as a general user and try to access localhost:4200/admin, the route will display the admin module. So how can we secure the routes for unauthorized access?

First, you need to store the user's information somewhere to identify the user. Let's keep the logged-in user information in session storage.

Inside the authenticateUser method in login.component.ts, add the following line of code to keep the user information in session storage:

sessionStorage.setItem("user", userName);


Here is how the authenticateUser method looks:

  authenticateUser(userName){
    sessionStorage.setItem("user", userName);
    if(userName == "admin"){
      this.router.navigate(['/admin']);
    } else if(userName == "manager"){ 
      this.router.navigate(['/manage']);
    } else if(userName == "general"){
      this.router.navigate(['/general'])
    }
  }


Now, create a service called routeGuard using the Angular CLI command:

ng g service routeGuard


In this Angular service, you'll implement the Angular CanActivate Guard interface to secure the Angular route.

Here is how the route-guard.service.ts file looks:

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

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

  constructor() { }

  public canActivate(route: ActivatedRouteSnapshot){
    return true;
  }
}


Add the above RouterGuardService to the routes defined in the app-routing.module.ts file's admin route.

{ 
    path: 'admin', 
    canActivate : [RouteGuardService],
    loadChildren: () => import('./administration/administration.module').then(m => m.AdministrationModule) 
}


Here is how the app-routing.module.ts file looks:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RouteGuardService } from './route-guard.service';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { 
    path: 'admin', 
    canActivate : [RouteGuardService],
    loadChildren: () => import('./administration/administration.module').then(m => m.AdministrationModule) 
  },
  { 
    path: 'general', 
    loadChildren: () => import('./general/general.module').then(m => m.GeneralModule) 
  },
  { 
    path: 'manage', 
    loadChildren: () => import('./management/management.module').then(m => m.ManagementModule) 
  },
  { path: '', redirectTo : 'login', pathMatch:'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }


The canActivate method in RouteGuardService returns true at the moment. Let's add the logic to allow access to the admin route for admin users.

  public canActivate(route: ActivatedRouteSnapshot){
    let user = sessionStorage.getItem('user');
    if(user == 'admin'){
      return true;
    }
    return false;
  }


You can get the user information from the session storage and return true if the user is admin.

Save the above changes and restart the Angular app. Log in as a general user and try to access the localhost:4200/admin/ route. You will be redirected to the Login page.

Using Angular guards, you can secure any of the other routes.

Wrapping It Up

You learned how to secure an Angular app route using route guards. Here, we only explored the Angular Guard's concept from a PoC point of view.

While implementing Angular guards, you can use other interfaces that Angular provides. Get an in-depth understanding by reading the Angular route guard's documentation.

Since we are talking about security, learn how to obfuscate and protect the source code of your Angular apps.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

Building an app with Angular & Firebase

In this tutorial, you will learn how to build a basic CRUD app using Angular and Firebase

August 26, 2021 | By Jay Raj | 11 min read

Section Divider

Subscribe to Our Newsletter