icon-search
sylwia-bartyzel-442-unsplash

Angular 2 Route Guards used in authorization: real life example

Piotr Wojnarowski 20.12.2016

During the later stages of Angular 2 development, the Beta and RC phases, one of the most dynamically changing module was the router. Since it seems that one very interesting feature – the Route Guards – has reached its final version, we’d like to write about it. In this post you’ll see the changes it went through and a real life example of Angular 2 Route Guards used in authorization.

Disclaimer: This article was written based on Angular 2.2 and Router 3.2. Because of that the examples shown here should work with later versions but will definitely fail with older router versions.

What are the Angular 2 Route Guards and what to do with them?

As the name suggests, you can configure guards on routes in your application to control how the user navigates between them. Those are functions called when router tries to activate or deactivate certain routes. We will focus on the CanActivate guard, but we will discuss the other guards later in the post. You can read about all of them in the  Angular Guard documentation.

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.

As you can imagine, it is basically a bad design when the application just hangs with an empty part of the page when user does not have access to something. Usually it’s best to redirect the user to some other route. This means that we have a side effect in our function. Furthermore the function is called “CanActivate” so we would expect that it will just return true or false. Unfortunately Angular doesn’t give us access to handling CanActivate rejection so this is the only way we have.

Defining Route Guards

There are two ways of defining a guard. A simpler one is just through creating a function, like below:

Route Guard as a function

// file app.routing.ts

const appRoutes: Routes = [
    {path: "", redirectTo: "board", pathMatch: "full"},
    {path: "board", component: BoardComponent, canActivate: ["boardGuard"]}
];

export const routingProviders = [
    {provide: "boardGuard", useValue: boardGuard}
];

export function boardGuard(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return true;
}
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
// file app.module.ts

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

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

Here you can see part of a routing file and a part of a module file. First, we define a route, “board”, that will display user’s dashboard. To that goal, we define the guard, by setting the canActivate attribute on the route: [“boardGuard”]. As you can see this attribute is an array, so you can define multiple guards, eg. to better split the responsibility. Router will iterate over all of them, unless one will deny the access.
In the following lines we define an Angular provider for the function, so that Angular dependency injection could recognize it. This array with single provider will be later used in app.module file in NgModule definition.
The function just returns true, nothing fancy so far, so user will always be able to activate the route.

You can also use the dependency injection in the guard function, like this:

export const tpRoutingProviders = [
    {provide: "authenticatedGuard", useFactory: (authService: AuthService) => {
            return () => authService.isUserAuthenticated();
        },
        deps: [AuthService]
    }
];

By using useFactory provider, we can define some dependencies of our function, allowing us to inject the AuthService. Then we just define a function that calls isUserAuthenticated() from that service. This function could return a boolean eg. by checking cookies or maybe an observable, getting data from a backend. Only after it resolves, the route will be activated.

Route Guard as a class

The second option is to define a class that implements the Can Activate interface. Let’s follow our example:

// file app.routing.ts

const appRoutes: Routes = [
   {path: "worksheet", component: WorksheetComponent, canActivate: [WorksheetAccessGuard]}      
];

// file ActivationGuards.ts

@Injectable()
export class WorksheetAccessGuard implements CanActivate {
   private static USER_PARAM = "userId";

   constructor(private router: Router, private userService: CurrentUserService) {}

   public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      const currentUser = this.userService.currentUser;
      const paramUser = route.params[WorksheetAccessGuard.USER_PARAM];
      if (paramUser && paramUser !== currentUser.id && !currentUser.admin) {
          this.router.navigate(["worksheet"]);
          return false;
      }
      return true;
   }
}

// file app.module.ts

@NgModule({
   providers: [WorksheetAccessGuard]
})
export class AppComponent {}

As we can see, the WorksheetAccessGuard class has method canActivate, which is used by the router in the same manner the function from previous example – it is called when the “worksheet” route is being activated and decides if it can happen. But this time we have also the constructor in which we can inject some services.

How does it work?

The worksheet component by default displays the work data of the current user. When accessed with user param set it can display data for another user. However this feature is only available for admin. As we can see, the canActivate function gets the current user from the injected CurrentUserService, then user param from ActivatedRouteSnapshot. If there is no param or the param user is the same as the current user or if the user is admin, then we let the component be activated. Otherwise we use the injected router to navigate into the user’s own worksheet, navigating without params.

We are using the method’s first argument of type ActivatedRouteSnapshot to get the this route params. This argument represents the state of the route that the users wants to activate. We can also get the url from it or the whole tree of routes in the current router.

Old router’s annotations

In the old router, the guards where setup by annotations. Just like RouteConfig, they were defined in decentralized way, in every navigable component, not in the NgModule:

@Component()
@RouteConfig([
   {path: "/userAccount", as: "UserAccount", component: UserAccountComponent},
   {path: "/accounts", as: "Accounts", component: OtherAccountComponent}
])
@CanActivate(() => AppConfigurationService.instance.waitForConfiguration())
export class WorkflowComponent {}

There was one big issue with that – only static functions and no dependency injection. This sometimes pushed developers for very creative solutions. In our case, we created a static field “instance” in the  AppConfigurationService. This field is set in the constructor of this service. We also inject the service into the main component of our application. This way it will be constructed before it will be needed for guarding. As you can see there is a lot of complications that we got rid off in the new router.

More complex example

Now let’s dive into the full example of how to guard activation on multiple routes.

Routing

const appRoutes: Routes = [
    {path: "login", component: LoginComponent},
    {path: "", redirectTo: "auth", pathMatch: "full"},
    {path: "expired", component: TrialExpiredComponent, canActivate: [AuthenticatedGuard]},
    {path: "auth/invite", component: InviteComponent, canActivate: [AuthenticatedGuard, AdminGuard]},
    {path: "company", component: CompanyPickerComponent, canActivate: [AuthenticatedGuard, CompanyPickerGuard]},
    {path: "auth", component: AuthComponent, canActivate: [AuthenticatedGuard, AuthComponentGuard],
        children: [
            {path: "", redirectTo: "ts", pathMatch: "full"},
            {path: "ts", component: TimesheetComponent, canActivate: [TimesheetAccessGuard]},
            {path: "projects", component: ProjectsComponent, canActivate: [AdminGuard]}
        ]
    }
];

There are multiple routes in our application. We can divide them into four groups:

  1. Login with no activation guard – everybody can access it
  2. TrialExpired that just requires user to be authenticated
  3. Invite and CompanyPicker components – both of them requires user to be authenticated, which is ensured by AuthenticatedGuard. Then they have some other guard that checks various requirements for every one of them.
  4. AuthComponent and its children. Whenever user opens one of them, first the router checks if AuthComponent can be activated and then it runs guards of the child component.

Warning

We haven’t found documentation for it but it seems that activation guards are called in the given order but don’t have to be resolved in that order. If the first guard returns false the second one won’t be called. But when the first one returns a Promise that will resolve to false in 5 seconds, then the second one will be called and processed and then router will wait for all for the guards to resolve. The same goes for guards defined in parent and child components – they are called in order but not necessarily resolved in that order.

Authenticated Guard

@Injectable()
export class AuthenticatedGuard implements CanActivate {
    constructor(private authService: AuthService, private router: Router) {}

    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (!this.authService.isAuthenticated()) {
            this.router.navigate(["/login"]);
            return false;
        } else {
            return true;
        }
    }
}

This is the most basic one – it just checks if the user is authenticated and then lets the component to activate or redirects to login.

AdminGuard

@Injectable()
export class AdminGuard implements CanActivate {
    constructor(private companyService: CurrentCompanyUserViewService) {}

    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.companyService.companyUser.admin;
    }
}

Admin guard is also very easy – it just checks whether the user is an admin or not.

AuthComponentGuard

@Injectable()
export class AuthComponentGuard implements CanActivate {
    constructor(private router: Router, private companyUserView: CurrentCompanyUserViewService) {}

    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (!this.companyUserView.companyUser) {
            this.router.navigate(["/company"]);
            return false;
        } else if (this.companyUserView.isTrialExpired) {
            this.router.navigate(["/expired"]);
            return false;
        } else {
            return true;
        }
    }
}

AuthComponent surrounds the main content of the application. User can be assigned to one or more companies. User can switch between them while browsing the application. To access the content of Auth Component, user needs to choose a company first. If not, the companyUserView.companyUser field will be undefined and router will navigate to “company” route.
Also user has to have an active subscription. If the trial is expired user will be redirected to “expired” route.
Remember that this route is used together with AuthenticatedGuard so inside AuthComponent we know that the user is authenticated.

CompanyPickerGuard

@Injectable()
export class CompanyPickerGuard implements CanActivate {

  constructor(private router: Router, private companyViewService: CurrentCompanyUserViewService) {}

   public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      if (this.companyViewService.isTrialExpired) {
          this.router.navigate(["expired"]);
          return false; 
      } else if (!this.companyViewService.companyUser) {
          return true;
      } else {
          this.router.navigate(["auth", "ts", {userId: this.companyViewService.companyUser.id}]);
          return false;
      }
   }
}

This component forces user to choose a company. It cannot be accessed if the company is already set – there is another component for that. In that case user is redirected to the timesheet. User also cannot have an expired trial.

TimesheetAccessGuard

@Injectable()
export class TimesheetAccessGuard implements CanActivate {
   private static USER_PARAM = "userId";

   constructor(private router: Router, private companyService: CurrentCompanyUserViewService) {}

   public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      const currentUser = this.userService.currentUser;
      const paramUser = route.params[TimesheetAccessGuard.USER_PARAM];
      if (paramUser && paramUser !== currentUser.id && !currentUser.admin) {
         this.router.navigate(["worksheet"]);
         return false;
      }
      return true;
   }
}

This is basically the same guard as shown in “Route Guard as a class” section – checking “userId” param from the route and comparing it to the current user privileges.

What’s out there besides CanActivate?

There are other Route Guards defined in Anngular. Lets describe them shortly:

  • CanActivateChildren – it works just like CanActivate but it guards all the child routes of the one it’s defined for. Often used for component-less routes.
  • CanDeactivate – can user leave the current route? Useful for reminding user to save the unsaved work.
  • Resolve – it allows to delay the activation of a route until we get some data.
  • CanLoad – guards access to the asynchronous module.

Summary

Overall, Route Guards in Angular are really a powerful and useful tool when you want to control your users’ navigation. It is important to know, how it works and how it can be combined with other Angular mechanisms such as dependency injection. In addition it is interesting to see how this concept has changed between Angular Router versions. Furthermore, we can see that the topic does not end on CanActivate – you can expect more posts on our blog when we tackle the topic of loading asynchronous modules or component-less routes.

comments: 0


Notice: Theme without comments.php is deprecated since version 3.0.0 with no alternative available. Please include a comments.php template in your theme. in /var/www/html/www_en/wp-includes/functions.php on line 3937

5 responses to “Angular 2 Route Guards used in authorization: real life example”

  1. I think your analysis about guards which are promises is almost right. I think guards are processed by something very similar to Promise.all, so that, as long as guards are true or resolved, the others continue running, but as soon as one is false or rejected then the wrapping Promise is immediately stopped. Thus this implementation closely matches what we expect from a short-circuit-able AND of all the guards.

    I think many developers don’t get this right and use resolve(false) when they really mean reject(“cannot activate route”).

  2. A lot of people need to attendance Spain. If you don’t visit Spain before, you must to do it. Spain – it is one of the countries which disposed in southwest part of European Union.

    The Kingdom of Spain it is the nation which compounds in European Union and NATO.
    There are a lot of popular cities in The Kingdom of Spain. They are: Madrid – the central city of The Kingdom of Spain, Barcelona, Valencia, Seville.

    Everyday a lot of tourists visit different cities in the Kingdom of Spain. In opposition for weather conditions, guests from Africa, USA, Japand like to hold on time in this country.
    How Ever, the Kingdom of Spain is lukewarm Kingdom, by this reason the temp in Februar does not drop below 10 ° C. As this nation located in Europe, most of visitors from European countries like to be in Kingdom of SPain in the December too.

    To look on datas about them you receive possibility at spainwalk.com, there are a lot of famous materials expo hotel valencia reviews about museums or other infrastructure in different Spanish cities.

  3. п»їInternet Marketing tips: what some internet marketers are not telling you

    The world of Internet marketing is dominated by the incredible men and women who are more than happy to share their success stories. Some admit that making money with the internet can sometimes feel like stealing candy from a baby. And some admit they are more than enough opportunities to make money online for everyone to get their share.
    But there are still some secrets and some psychological marketing techniques that marketers use the Internet.
    They want you to feel like a stranger
    Marketing is based on the idea of trying to sell something you may or may not need. This principle of exact rules in the world of Internet marketing, too. However, instead of saying that the sellers know exactly what their product can or can not do, everyone uses words and phrases that makes it seem like they are on the inside. “” They are the ones all the secrets. And unfortunately, you are the outside walls. And like buying Microsoft stock in the 1980’s or Apple’s stock a few years ago, if you buy their product, it is likely going to lose the financial bonanza again. (Or at least that’s what they want you to think.)
    Earning money from their lists
    Another important thing to remember is that most of these internet marketers do not make money simply by landing a person on their website and by accessing an affiliate product you are promoting. Hardly. These guys often talk about the power of their lists, but rarely connect the dots for you. The next time you land on a page extolling the success of an Internet marketer, pay attention to the Clickbank screenshot sharing. Profits are rarely consistent. In fact, often start huge and then decrease significantly. This is usually due to a vendor launched its product. And the first place that started it? Your list.
    They make money by promoting non-internet marketing products
    If you’ve spent some time researching the world of internet marketing gurus selling their products make it appear that the only way to make money in Internet marketing is internet marketing. Although there is no money involved in that specific place, the fact is that many of the men and women who are titans in the field also money that the sale and promotion of products in other niches. They have been successful and have developed a strategy to replicate in other niches. It is important to take this into account. Rather than attempt to duplicate the process, step back and look at the big picture. The process that leads to success in a niche will probably bring a lot of success in many other niches.
    But here’s the kicker: Although some traders are trying to keep the “secrets” to earn a passive income you are smart, the vast majority can not be more explicit in what they are saying. Once you have purchased a product, many vendors say, “The use of this process, but do not use this example as I am sure that with this example, the market is saturated.”

    Visit site: http://gameone.club/

    branded surveys mintvine rewards

  4. п»їHow to Understand Consumer Behavior using Market Research

    Whenever new venture is opened or new product is launched successfully, market research acts as a backbone for it. Frankly speaking it is a helpful device to relate company capabilities with market opportunities and constraints. It gives detailed information of current and future market trends. In addition to that it helps company to serve its customers best way by knowing their pace of satisfaction with company’s products and services. Well done market analysis helps to know behavior of potential marketplace i.e. it requirements, likes, choices, demands for different types of products with varied features. A crucial information on consumer behavior helps to model marketing strategies and other business decisions. It encourages ventures to shape ideas to compete with rivals by means of better products that go well with targeted consumers and climbing profits.
    In order to understand consumer behavior, one must collect data regarding present trade drifts, life style of target buyers, external conditions and opportunities, competitive strategies of rival companies to satisfy potential customers etc. All these data should be included in market research report. Customer’s buying behavior mainly depends on its needs of products and services. These needs are either developed by their life style, economic factors or may be generated by product itself. Understanding consumer buying behavior can be helpful in order to serve and gratify them properly, which in turn raises earnings of company. Market research study facilitates to identify potential marketplace for particular products in specific geographic location, for any group of consumer during definite time interval.
    Besides knowing to market opportunities, research work also aids to find barriers in competing with strong players. Market analysis benefits company to curtail loss making strategies and deciding to continue with the same product or produce new ones. Perhaps it gives solutions to problems those could bump into effect with a drastic loss, and approve plentiful time to correct errors before new product launch.
    While collecting data for study purpose, it is collected in two ways. Quantitative data gives figures whereas qualitative data provides cause and effect analysis. Figures are used to compare various data and decide which option is better than others. Qualitative data is gathered by analyzing vague data from various surveys, arranging well in structured form which clearly shows logic behind it. It gives an idea about behavior of consumers, their choices and preferences and reasons for it such as economic and socio-cultural effects. Using research data brand new strategies are developed by companies to enhance consumer base by attracting them towards company and its products. They can design products suitable to current needs of consumers, decide marketing strategies, earn better profits and accordingly improve consumer attitude towards company through brand image.
    These are some of the benefits to resolve success and failure factors of companies and to better know consumer behavior. In that sense market research can be considered as a device to shoot problematic situation and expand earning vicinity.

    Visit site: http://gameone.club/

    copywriting dewa eka prayoga

Leave a Reply

Your email address will not be published. Required fields are marked *