Secure the Apps with UrlTree Parsed Routes

Angular 10/9 Securing the UI with canActivate RouteGuard and UrlTree Parsed Routes

Now we are going to implement,   

Angular 10 Guards

 how to use Router Guards and UrlTree data structures to protect the UI if the user is not logged in and redirect them to the login interface if they don't have access to a specific route.


The admin interface must be only accessed by the website owner so we need to use Guards to protect the components of the admin module and only allow access to them if the user is logged in.

first we need to create a Guard, Run the following command that will generate Guard service with two files ( guard.spec & guard.ts )

ng g guard admin/admin

Within src/app/admin/admin.guard.ts file, you should see the following code:

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

@Injectable({
  providedIn: 'root'
})
export class AdminGuard implements CanActivate {
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return true;
  }
}

 From the above code, you see that a guard is simply a service that implements the CanActivate interface and overrides the canActivate() method. In this case, it always returns true which means access will be always granted to the user when this guard is applied to a route.

Note :  Resolver is also a guard used for doing operations just before route activation.

Let's change the method to only allow access if the user is logged in. First, you need to import AuthService and inject it via the AdminGuard service and next you need to call the isLoggedIn property in the canActivate() method to check if the user is logged in and return true or false; 


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

import { AuthService } from './auth/auth.service';

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

  constructor(private authService: AuthService){}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
      return this.authService.isLoggedIn;
  }
}
The canActivate() method will return true if the user is logged

The canActivate() method is passed many arguments which makes it easy to detrmine if the guard needs to allow or disallow access to certain route(s):

  1. next: ActivatedRouteSnapshot which is the next route that will be activated if the guard is allowing access,
  2. state: RouterStateSnapshot which is the next router state if the guard is allowing access.
Now, you need to apply the guard to the routes you need to protect using the canActivate property of the path object. Open the src/app/admin/admin-routing.module.ts file and update it accordingly:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { ProjectComponent } from './project/project.component';
import { ProjectListComponent } from './project-list/project-list.component';
import { ProjectCreateComponent } from './project-create/project-create.component';
import { ProjectUpdateComponent } from './project-update/project-update.component';
import { LoginComponent } from './login/login.component';
import { AdminGuard } from  './admin.guard';

const routes: Routes = [
    {
        path: 'admin',
        component: ProjectComponent,
        children: [
            {
                path: 'list',
                component: ProjectListComponent,
                canActivate: [AdminGuard]
            },            
            {
                path: 'create',
                component: ProjectCreateComponent,
                canActivate: [AdminGuard]
            },
            {
                path: 'update',
                component: ProjectUpdateComponent,
                canActivate: [AdminGuard]
            },
            { 
                path: 'login', 
                component: LoginComponent 
            }
        ]
    }
];


@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class AdminRoutingModule { }
Now you have already viewed,
We successfully protected the ProjectListComponentProjectCreateComponent and ProjectUpdateComponent components of the admin module from non logged in users.

Note: The canActivate property of the path object takes an array which means you can register multiple guards.

Before Angular 7.1, route guards can only return a boolean, Promise<boolean> or Observable<boolean> (asynchronous boolean objects) to tell the router if the route can be activated or not. But now, you can also return an UrlTree variable which provides the new router state (route) that should be activated.

According to the Angular docs an UrlTree is a data structure that represents a parsed URL.

Note: You can create an UrlTree by calling the parseUrl() or createUrlTree() method of the Router object.


Now, let's change our router guard to redirect the users to the /admin/login route if they try to access the protected admin components without being logged in first:

  • First, we import and inject the router,
  • Next, we update the canActivate() method to return an UrlTree corresponding to the /admin/login route.

This is the full code of the AdminGuard service:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth/auth.service';

@Injectable({
  providedIn: 'root'
})
export class AdminGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router)
  {}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
    if(this.authService.isLoggedIn){
      return true;
    }
    else{
      return this.router.parseUrl("/admin/login");
    }

  }
}

We check if the user is logged in and we return true, otherwise we return the result from the parseUrl("/admin/login") method of the injected router instance which is an UrlTreeof the /admin/login route.

Now, go to your application, if you visit any protected route without logging in you will be redirected to the /admin/login route where you can login.


Comments

Popular posts from this blog

Become a Expert + Secret of Success -- ii

Change Detection

without writing media queries How to make responsive component