While creating a web app, setting up permissions based on roles is a common scenario. Some users may have permissions to certain pages or certain sections of the pages depending on their user role.
In this tutorial, you'll learn how to create a role-based app using Angular route guard. The source code from this tutorial is available at GitHub.
Setting Up the App
Let's start by setting up the Angular app. Assuming that you already have the Angular CLI installed, let's 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 module using the command below. (The --routing
option creates the routing file for each of the consecutive modules.)
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, let's also create a Login component.
ng g component login
Now 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.
Let's implement the logic for the Login component which we'll use for securing our Angular application routes.
Implementing the Login Component
Let's 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 username as shown above is not a secure way of authentication. This is for demo purposes only. For more information on secure authentication, check this tutorial.
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 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 only allow access to the admin route by 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, else false
.
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.
Similarly, using Angular guards you can secure any of the other routes.
Wrapping It Up
In this tutorial, you learned how to secure an Angular app route using route guards. Here, we only explored the Angular guards concept from a PoC point of view.
While implementing Angular guards, you can use other interfaces that Angular provides. To get an in-depth understanding, we recommend reading the Angular route guard's documentation.
And since we're talking about security, learn how you can obfuscate and protect the source code of your Angular apps by following our guide.