Robust Role Based Routing in AngularJS

James Allardice on

The role of roles

Roles are quite an important concept in most web applications. Not all users will have access to everything and it should be easy from a development point of view to restrict access to parts of the app from certain users.

In an AngularJS application you have a choice of a couple of popular routing modules, none of which contain support for role-based routing. We wanted a solution that builds on top of the ngRoute module to allow us to specify different routes for different users.

The ngRoute module

The AngularJS team maintains an "official" routing solution in the ngRoute module. If you've ever built an Angular app you'll probably be familiar with its configuration:

In the above example we define a couple of routes. When the path of the URL in the browser matches one of the strings passed to the route provider the relevant template will be displayed. This means we can use normal links throughout our templates and Angular will take care of the rest:

The above template shows two different ways of handling routes within markup. The ngHref directive allows you to use Angular's interpolation syntax to dynamically insert a value into the URL and the normal href attribute allows you use a simple string.

There are three main deficiencies with this approach. Firstly, you have to know the exact URL for every view of your application. Secondly, if a URL was to change you'll have to search through your entire codebase and replace every occurrence of the old string with the new one. Finally, if the current user is not able to access a particular view then there will have to be logic somewhere to handle this.

Typically there are three common ways of doing so, all of which exhibit some problems:

  • You can place some logic at the top of the controller of the restricted route. This is not a great solution because the route change is allowed to go ahead and the user will experience a flash of the empty new template before quickly being redirected elsewhere.

  • You can use a location change event handler and prevent the default action if it should not be allowed. This approach is poorly documented and difficult to get working. Of particular confusion is the inability to use a route change event handler.

  • You can define different routes for each type of user and use the ngIf or ngShow directives to display different links appropriately. This leads to bloated markup and the potential for users to briefly see links that they shouldn't if you don't properly cloak them until your controller is ready.

Introducing the mmRoute module

We built the mmRoute module with those three concerns in mind. Instead of attempting to be a complete routing solution it builds on top of ngRoute which means you already know most of what you need to configure it and it benefits from the stability of code that is used in production across the web.

The service is configured via its provider in your app config just as the ngRoute service is. However, instead of focusing on URLs the configuration focuses on roles. Roles are configured via the provider too and would generally come from the server. For example, when the user logs in to your app you could generate a JavaScript snippet containing an Angular module config function and output it onto the page.

Notice how in the above example the mmRoute service is exposed on the root scope. Because Angular scopes prototypically inherit from the root scope this means we are able to access mmRoute as routes in any descendant scope. We can rewrite the "account" view from above as follows:

This time we don't have any hard-coded URLs. Instead we ask the mmRoute service to get the relevant URL for each feature based on the roles available to the active user.

You can install the module through Bower with bower install ng-mm-route and it's also available on GitHub. We're always happy to hear feedback and accept improvements to our projects. Don't hesitate to fork it and send us back pull requests if you have any modifications.