Click here to Skip to main content
15,867,594 members
Articles / Mobile Apps / Android
Tip/Trick

AngularJS Security - Authorization on Angular Routes

Rate me:
Please Sign up or sign in to vote.
4.83/5 (18 votes)
26 Aug 2014CPOL4 min read 154.9K   44   17
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. In this example, I have considered only Windows Authentication, for sake of simplicity. 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.

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
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
         })
});

Summary

We saw in this tip as to how we can define authorization on various routes of Angular JS based application. As an alternative to securing routes, you may choose to not show the link (Or Menu links) to user who does not have access to it, however, putting the access on route itself at a central place in application ensures that even if you miss hiding a link (which ultimately becomes a route) for unauthorized user, the route configuration will ensure that unauthorized user will not be taken to the route.

Lastly, but importantly, I would like to emphasize that, all of the above stands invalid if the user tricks the browser. Therefore, it is important to have server side check for any read/update of data.

License

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


Written By
India India
I am passionate about technology and love to blog.

Comments and Discussions

 
QuestionNot being redirected again when going back after redirect Pin
Member 1083449019-Dec-16 21:39
Member 1083449019-Dec-16 21:39 
AnswerRe: Not being redirected again when going back after redirect Pin
Pramod Sharma Tech31-Dec-16 4:51
Pramod Sharma Tech31-Dec-16 4:51 
GeneralRe: Not being redirected again when going back after redirect Pin
Member 1083449017-Jan-17 7:42
Member 1083449017-Jan-17 7:42 
GeneralRe: Not being redirected again when going back after redirect Pin
Pramod Sharma Tech19-Feb-17 6:04
Pramod Sharma Tech19-Feb-17 6:04 
QuestionCool Pin
Anatoliy Khotin (9682296)22-Jun-16 9:22
Anatoliy Khotin (9682296)22-Jun-16 9:22 
QuestionWorking the same project code with Spring Pin
Nagasanthosh Varanasi9-Nov-15 15:27
Nagasanthosh Varanasi9-Nov-15 15:27 
QuestionRe: Working the same project code with Spring Pin
Project Managment4-Apr-16 22:11
Project Managment4-Apr-16 22:11 
QuestionHave this example github or other source code share url ? Pin
sargin4826-Jan-15 23:12
sargin4826-Jan-15 23:12 
QuestionlocationChangeSuccess increments Pin
Bearsneezed29-Oct-14 0:06
Bearsneezed29-Oct-14 0:06 
Questionangular spa secure routing Pin
Member 1013866423-Sep-14 0:04
Member 1013866423-Sep-14 0:04 
AnswerRe: angular spa secure routing Pin
Pramod Sharma Tech2-Oct-14 10:54
Pramod Sharma Tech2-Oct-14 10:54 
QuestionAnother tiny problem in the format of your code Pin
Nelek12-Sep-14 1:40
protectorNelek12-Sep-14 1:40 
QuestionCode indentation Pin
Nelek26-Aug-14 23:52
protectorNelek26-Aug-14 23:52 
AnswerRe: Code indentation Pin
Pramod Sharma Tech11-Sep-14 6:45
Pramod Sharma Tech11-Sep-14 6:45 
GeneralRe: Code indentation Pin
Nelek11-Sep-14 7:22
protectorNelek11-Sep-14 7:22 
Questionmy vote of 4 Pin
tyjnfghnfgsdf26-Aug-14 2:27
tyjnfghnfgsdf26-Aug-14 2:27 
AnswerRe: my vote of 4 Pin
Pramod Sharma Tech11-Sep-14 6:22
Pramod Sharma Tech11-Sep-14 6:22 

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

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