Click here to Skip to main content
Click here to Skip to main content

AngularJS Security - Authorization on Angular Routes

, 26 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Securing routes in Angular is not available out of the box. This tip would help in securing routes in Angular based applications.

Introduction

AngularJS has come a long way since its introduction. It is a comprehensive JavaScript framework for Single Page Application (SPA) development. It has some awesome features like 2-way binding, directives, etc. This topic will focus on Angular security. This is client side security which you can implement with Angular. In addition to client side route security (which we would discuss in this tip), you would need to secure access at server side as well. Client side security helps in avoiding extra round trips to server after making user session object at client. However, if someone tricks the browser, then server side security should be able to reject unauthorized access. In this tip, I would restrict my discussion to client side security.

We would make one authorization service, which would fetch the User authorization/role specific data from server about a user role. And, later, this user role data would be reused for all routes visited by user, during his session. A very interesting aspect which you would notice about angular service is that, service data is persisted throughout the user session. To understand this post, you need basic knowledge of AngularJS , Angular Routing and promises. I will walk you through, step by step, on the details of the implementation. It is not something I have invented new in angularJS. However, I have tried to put together the things which are available in Angular and can be combined to make a re-usable security code. I am open to suggestions for this article write-up improvement. Please let me know if you see anything which is incorrect or missing. I will make the corrections as necessary. Hope you enjoy reading it!

Step 1: Define Global Variables in app-module

Define roles for the application:

These are the roles which you would like to keep for authorization in the application. It is necessary to keep them global as these would be used by both at App module scope and service scope.

      var roles = {
            superUser: 0,
            admin: 1,
            user: 2
        };

Define route for unauthorized access for the application:

This is the route where you would want the user to go, if he is not authorized to see any page.

We would be using this variable later, in the App module routes.

     var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';

Step 2: Define the Service for Authorization

This is the heart of the whole authorization. Angular service (or factory) is singleton and persisted across different routes of application, for a given user session. We can leverage this feature of service to do authorization on different routes. In order for you to create the service, you would need a service (like Web API) at server side, which can give you a set of roles for a given user. Once roles are procured from server side, then angular service can persist this knowledge to be re-used for all routes, during the session of user. I have tried to explain the service code below with inline comments.

appModule.factory('authorizationService', function ($resource, $q, $rootScope, $location) {
    return {
        // We would cache the permission for the session,
        //to avoid roundtrip to server
        //for subsequent requests

        permissionModel: {
            permission: {},
            isPermissionLoaded: false
        },

        permissionCheck: function (roleCollection) {

            // we will return a promise .
            var deferred = $q.defer();

            //this is just to keep a pointer to parent scope from within promise scope.
            var parentPointer = this;

            //Checking if permission object(list of roles for logged in user) is already filled from service
            if (this.permissionModel.isPermissionLoaded) {
                //Check if the current user has required role to access the route
                this.getPermission(this.permissionModel, roleCollection, deferred);
            } else {
                //if permission is not obtained yet, we will get it from  server.
                // 'api/permissionService' is the path of server web service , used for this example.

                $resource('/api/permissionService').get().$promise.then(function (response) {
                    //when server service responds then we will fill the permission object
                    parentPointer.permissionModel.permission = response;

                    //Indicator is set to true that permission object is filled and 
                    //can be re-used for subsequent route request for the session of the user
                    parentPointer.permissionModel.isPermissionLoaded = true;

                    //Check if the current user has required role to access the route
                    parentPointer.getPermission(parentPointer.permissionModel, roleCollection, deferred);
                });
            }
            return deferred.promise;
        },

        //Method to check if the current user has required role to access the route
        //'permissionModel' has permission information obtained from server for current user
        //'roleCollection' is the list of roles which are authorized to access route
        //'deferred' is the object through which we shall resolve promise
        getPermission: function (permissionModel, roleCollection, deferred) {
            var ifPermissionPassed = false;

            angular.forEach(roleCollection, function (role) {
                switch (role) {
                    case roles.superUser:
                        if (permissionModel.permission.isSuperUser) {
                            ifPermissionPassed = true;
                        }
                        break;
                    case roles.admin:
                        if (permissionModel.permission.isAdministrator) {
                            ifPermissionPassed = true;
                        }
                        break;
                    case roles.user:
                        if (permissionModel.permission.isUser) {
                            ifPermissionPassed = true;
                        }
                        break;
                    default:
                        ifPermissionPassed = false;
                }
            });
            if (!ifPermissionPassed) {
                //If user does not have required access, we will route the user to unauthorized access page
                $location.path(routeForUnauthorizedAccess);
                //As there could be some delay when location change event happens, 
                //we will keep a watch on $locationChangeSuccess event
                // and would resolve promise when this event occurs.
                $rootScope.$on('$locationChangeSuccess', function (next, current) {
                    deferred.resolve();
                });
            } else {
                deferred.resolve();
            }
        }
    };
});

Step 3: Use Security in Routing

Let's use all our hard work done so far to secure the routes. So far, we created a service which is capable of checking if a user falls under a particular role or not. This knowledge can be used in the angular routes now. In the angular module, where we define routes, we can put these checks to see if the user has access to any of the roles specified for a route. I have put combinations of roles in these routes, to ensure better understanding.

     var appModule = angular.module("appModule", ['ngRoute', 'ngResource'])
     .config(function($routeProvider, $locationProvider) {
         $routeProvider
             .when('/superUserSpecificRoute', {
                 templateUrl: '/templates/superUser.html', //path of the view/template of route
                 caseInsensitiveMatch: true,
                 controller: 'superUserController', //controller which would be used for the route
                 resolve: { //Here we would use all the hardwork we have done 
                     //above and make call to the authorization Service 
                     //resolve is a great feature in angular, which ensures that a route 
                     //controller (in this case superUserController ) is invoked for a route 
                     //only after the promises mentioned under it are resolved.
                     permission: function(authorizationService, $route) {
                         return authorizationService.permissionCheck([roles.superUser]);
                     },
                 }
             })
             .when('/userSpecificRoute', {
                 templateUrl: '/templates/user.html',
                 caseInsensitiveMatch: true,
                 controller: 'userController',
                 resolve: {
                     permission: function(authorizationService, $route) {
                         return authorizationService.permissionCheck([roles.user]);
                     },
                 }
             })
             .when('/adminSpecificRoute', {
                 templateUrl: '/templates/admin.html',
                 caseInsensitiveMatch: true,
                 controller: 'adminController',
                 resolve: {
                     permission: function(authorizationService, $route) {
                         return authorizationService.permissionCheck([roles.admin]);
                     },
                 }
             })
             .when('/adminSuperUserSpecificRoute', {
                 templateUrl: '/templates/adminSuperUser.html',
                 caseInsensitiveMatch: true,
                 controller: 'adminSuperUserController',
                 resolve: {
                     permission: function(authorizationService, $route) {
                         return authorizationService.permissionCheck([roles.admin, roles.superUser]);
                     },
                 }
             })
             // Route for unauthorized access (When permission is not given to visit a page)
             .when(routeForUnauthorizedAccess, 
                {
                 templateUrl: '/templates/UnauthorizedAccess.html',

                caseInsensitiveMatch: true
              })
     });

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

I am passionate about technology and love to blog.

Comments and Discussions

 
QuestionlocationChangeSuccess increments PinmemberBearsneezed29-Oct-14 0:06 
Questionangular spa secure routing PinmemberMember 1013866423-Sep-14 0:04 
AnswerRe: angular spa secure routing PinmemberPramod Sharma Tech2-Oct-14 10:54 
QuestionAnother tiny problem in the format of your code PinmemberNelek12-Sep-14 1:40 
QuestionCode indentation PinmemberNelek26-Aug-14 23:52 
AnswerRe: Code indentation PinmemberPramod Sharma Tech11-Sep-14 6:45 
GeneralRe: Code indentation PinmemberNelek11-Sep-14 7:22 
Questionmy vote of 4 Pinmembertyjnfghnfgsdf26-Aug-14 2:27 
AnswerRe: my vote of 4 PinmemberPramod Sharma Tech11-Sep-14 6:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141030.1 | Last Updated 26 Aug 2014
Article Copyright 2014 by Pramod Sharma Tech
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid