Handling authorization

Like authentication, authorization support too needs to be implemented on both the server and client side; more so, on the server than the client. Remember, anyone can hack into the JavaScript code and circumvent the complete authentication/authorization setup. So always tighten your server infrastructure irrespective of whether the client has the necessary checks in place or not.

This still does not mean that we do not do an authorization check on the client. For standard users, this is the first line of defense against unwarranted access.

When working on an authorization requirement for any application, there are three essential elements that are part of the setup:

  • The resources that need to be secured/authorized
  • A list of roles and users that are part of these roles
  • A mapping between the resources and the roles that defines who can access what

From an Angular app perspective, the resources are the pages, and sometimes sections of pages, that need to be restricted to specific roles. If the user is in a specific role, depending upon the role-resource mapping he/she gets access to some pages, else he/she is denied access.

While authorization in Angular applications can be implemented in a number of ways, we will outline a generic implementation that can be further customized to suite our needs in the future.

Adding authorization support

To enable authorization, the first thing that we need to do is to expose the logged-in user data including roles throughout the application.

Sharing user authentication context

Using the Angular service to share the authentication context perfectly fits the bill. Look at the outline of the service here:

angular.module('app').factory('sessionContext', function () {
    var service;
    service.currentUser = function () {...}
    service.isUserInRole = function (roles) {...}
    service.authenticated = false;
    return service;
});

The sessionContext service tracks the user login session and provides details such as the logged-in user (currentUser), whether the user is authenticated (authenticated), and the isUserInRole function that returns true or false based on whether the user is part of any of the roles passed into the roles parameter.

With this service in place, we can add authorization for routes (views), thereby restricting access to some routes to specific roles only.

Restricting routes

To restrict route access, we can use the route configuration to define what roles have access to which routes, and hence views. Here is how a sample route configuration with role-access defined looks:

$routeProvider.when('/admin', {
     templateUrl: 'admin.html',
     controller: 'AdminController',
     roles:['admin']
 });
$routeProvider.when('/home', {
     templateUrl: 'home.html',
     controller: 'HomeController',
     roles:[ 'admin', 'user']
 });

The new roles property on the route declares who has access to the specific view. This sets up the mapping between role and view access, but it still needs to be enforced.

To make sure the route is not loaded for an unauthorized user, we can catch the $routeChangeStart event in either a root controller or the module run function and redirect the user to an unauthorized page appropriately. This is a rough draft of how the code that does this redirection should look:

angular.module('app').run(function ($rootScope, $location, SessionContext) {
    $rootScope.$on('$routeChangeStart', function (event, next) {
        if (next.roles && !SessionContext.authenticated) {
            $location.path('/login');  //needs to login
        }
        if (next.roles && SessionContext.authenticated && 
        !SessionContext.isInRole(next.roles)) {
            $location.path('/unauthorized'); //un-authorized
        }
    });
});

We subscribe to the $routeChangeStart event and in the event handler look at the roles property of the next route to establish whether the user is allowed to access the route or not. If not, we redirect the user to either the login or unauthorized page.

This fixes the routes, but there is one more thing to fix. What happens when a page has view elements that are rendered based on the user’s role?

Conditionally rendering content based on roles

Conditionally rendering content is easy to implement. We just need to show/hide HTML elements based on the user role. Something like this works:

<div  id='header'>
    <div> Welcome, {{userName}}</div>
    <div><a href='#/setting/my'>Settings</a></div>
    <div ng-if='isUserInRole(["admin"])'>
<a href='#/setting/site'>Site Settings</a>
</div>
</div>

The preceding code checks whether the user is in an admin role before rendering a Site Setting hyperlink.

What we have previously outlined is a reference implementation to achieve authentication and authorization. With this basic understanding in place, any setup can be tweaked to handle other custom authentication/authorization scenarios.