Angular 2

Angular2 Guards

In Angular2 one of the most changing module is Router and since it seems that one very interesting feature is Auth Guards. We will see the live example of Angular2 Route Guards in Authorisation.

What is Angular 2 Route Guards

As name suggests, you can restrict the routes in your application to control how user will navigate to between them, as we want to prevent our users from accessing areas that they’re not allowed to access. These guards are the functions called when user tries to navigate to routes. So that in this function we can write our logic to handle authorisation. We will focus more on the CanActivate guard.

The general rule is that the guards are functions that are called in certain points of the router lifecycle. They return a Boolean or an asynchronous response (Promise or Observable). In the case of CanActivate, the guard function is called when user tries to navigate into the route. The component behind it will only be activated after the function returns true or the Observable / Promise will eventually return true. When the function hangs or returns false, the router will not display the route content. But in most of the case application should be redirect to default route (like login), It is basically bad when application just hang-ups with blank page.

Guard Types

There are five different guard types we can use to protect our routes

Types Usages
CanActivate Decides if a route can be activated
CanActivateChild Decides if children route of a route can be activated
CanDeactivate Can user leave the current route? Useful for reminding user to save the unsaved work.
CanLoad Decides if a module can be loaded asynchronously
Resolve It allows to delay the activation of a route until we get some data.

Depending on what we want to do, we might need to implement one or the other guard.

Defining Guard

Guards can be implemented in different ways, but after all it really boils down to a function that returns either Observable, Promise or boolean.

  • CanActivate Guard
    There are two ways of defining a guard. A simpler one is just through creating a function, like below:
    1. As Function
// file app.routing.ts  
   
const appRoutes: Routes = [
    {path: "", redirectTo: "home", pathMatch: "full"},
    {path: "home", component: HomeComponent, canActivate: ["authGuard"]}
];   
export const routingProviders = [{  
    provide: "authGuard",  
    useValue: authGuard
}];   
export function authGuard(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {    
    return true;
}  
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

First, we define a route, “home”, that will display home page. To that goal, we define the guard, by setting the canActivate attribute on the route: [“authGuard“]. As you can see this attribute is an array, so we can define multiple guards, e.g. authenticateGuard, adminGuard, userGuard to better split the Authorisation. Router will iterate over all of them, unless one will deny the access. In the very next line we have define an Angular provider for the function, so that Angular dependency injection could recognise it. This array with single provider will be later used in app.module file in NgModule definition like below:

// file app.module.ts  
   
import {routing, routingProviders} from "./app.routing"; 

@NgModule({     
    import: [routing],  
    providers: [routingProviders]  
}) 
export class AppComponent {}

2. As Class
The second option is to define a class that implements the CanActivate interface. Sometimes, a guard needs dependency injection capabilities. In these cases, it makes sense to define a guard as a class, because dependencies can then be simply injected. Let’s say we want to protect a route and have the user authenticate first. We might want to inject an AuthService to determine if the user is authenticated or not. A class guard would be a perfect fit.
When creating a guard class, we implement either the CanActivate, CanDeactivate, or CanActivateChild interface, which requires us to have a method canActivate(), canActivateChild(), or canDeactivate() respectively. Those methods are pretty much the equivalent of a guard function in the previous scenario. The following snippet shows a simple CanActivate guard implementation using classes. Let’s see the example.

// file app.routing.ts  
// home route with authguard as a canActive guard
const appRoutes: Routes = [   
   {path: "home", component: HomeComponent, canActivate: [authGuard]}
];
  
// file authGuard.service.ts  
// This guard will check whether user is authenticated or not 
@Injectable() 
export class authGuard implements CanActivate {     
      
    constructor(private router: Router, private authService: AuthService) {}     
 
    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {    
    return this.authService.isLoggedIn();     
    }  
}  

// file app.module.ts  
@NgModule({     
    providers: [authGuard]  
})
export class AppComponent {} 
  • Multiple Guards
    To make our application more secure then it is good to implement multiple guards, consider in your application you have the requirement that user should be authenticated, for some pages you have authorisation in this case create the multiple guards as per the roles in your application.
    So let’s dive into the full example of how to guard activation on multiple routes. There are multiple routes in our application. We can divide them into four groups:
    Login with no activation guard – everybody can access it.
    Home that just requires user to be authenticated.
    Payment that requires user to be authenticated and should have user as a role.
    Transaction that requires user to be authenticated and should have user as a admin.

Let’s see the Routing

// file app.routing.ts  
// Application routes with guards
const appRoutes: Routes = [
   {path: "", component: LoginComponent}
   {path: "login", component: LoginComponent},
   {path: "home", component: HomeComponent, canActivate: [authGuard]},
   {path: "payment", component: PaymentComponent, canActivate: [authGuard, userGuard]},
   {path: "transaction", component: TransactionComponent, 
                canActivate: [authGuard, adminGuard]}
]; 

In above routes, default route is Login and it does not require any authentication so everybody can access it.
For home we just required the user should only have logged in.
For payment we require user to be authenticated, which is ensured by authGuard and then they have role as USER, which is ensured by userGuard.
For transaction we require user to be authenticated, which is ensured by authGuard and then they have role as ADMIN, which is ensured by adminGuard

// file userGuard.service.ts  
// This is userGuard to authorise user
@Injectable() 
export class userGuard implements CanActivate {     
      
    constructor(private router: Router, private userService: UserService) {}     
 
    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {    
    return this.userService.isUserRole();     
    }  
}
  
// file adminGuard.service.ts  
// This is userGuard to authorise admin
@Injectable() 
export class adminGuard implements CanActivate {     
      
    constructor(private router: Router, private adminService: AdminService) {}     
 
    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {    
    return this.adminService.isAdminRole();     
    }  
}  
  • CanDeactivate Guard
    We’ve now seen how CanActivate can work in different scenarios, but as mentioned earlier, we have a few more guard interfaces we can take advantage of. CanDeactivate gives us a chance to decide if we really want to navigate away from a route. This can be very useful, for example we want to prevent our users from losing unsaved changes when filling out a form and accidentally clicking on a button to cancel the process.
    Implementing a CanDeactivate guard is very similar to implementing a CanActivate guard. All we have to do is to create again, either a function, or a class that implements the CanDeactivate interface.

    // This is deactiveGuard to restrict from navigation to different route
    import { CanDeactivate } from '@angular/router';  
    import { HomeComponent } from './app/home-component';  
    
    export class ConfirmDeactivateGuard implements CanDeactivate {  
        canDeactivate(target: HomeComponent) {  
            if (target.hasChanges()) {  
                return window.confirm('Do you really want to cancel?');  
            }  
            return true;  
        }  
    }

    here’s one thing that we didn’t see in the previous example. CanDeactivate uses a generic, so we need to specify what component type we want to deactivate.
    Now we need to register CanDeactive Guard

    // file app.module.ts 
    // This is to register ConfirmDeactiveGuard
    @NgModule({
        ...
      providers: [...ConfirmDeactivateGuard]  
    }) 
    export class AppModule {} 
    

Once we are done with this our application is now ready. The final UI looks like this:
1. Login Screen
login.png

2. After Login as User – Home Page  homepage.png

3. After Click on Transaction Menu ( User Role)  transaction.png
Because currently we have logged in as User Role and Transaction is accessible to admin role only and this will redirect to login page.

4. After Click on Payment Menu (Admin Role) payment.png
Now Log in as Admin role and then click on Payment this will also give alert message and will redirect to login page because we have logged in as Admin and Payment is accessible to user role only

5. After Click on Payment Menu (User Role) navigate.png
Now again Login as User role and click on payment menu and then again click on home menu this will give you the alert message as “Do you really want to navigate away from the page?” because we have added CanDeactivate guard so this will restrict user from navigating to other routes.

Download sample code from zCon Solutions Github Repo


Written by Dnyaneshwar Uttarwar, Software Engg. at zCon Solutions

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s