Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle role based authorization in AngularJS?

I am creating a web app which will satisfy two requirements for the users. Note: I am new to AngularJS as a web development platform.

Front-end - 1: Its a search functionality where users can search for specific documents and studies based on keyword search and filters. This has been implemented using MySQL for fetching data and display using AngularJS.

Front-end - 2: Users will have the option to create a account on the web app. Purpose of the account is:

  1. Save their search queries.
  2. If the administrator associates each user with a specific role, then those users will get access to extra options such as modifying the documents present in the database as well as uploading new documents and host of other pages.

My question:

How to handle role based authorization in AngularJS? I am not able to figure out how to create a framework which involves following functionalities: - Users get a role associated to them - Prevent users from accessing pages or functionalities which are not associated with those roles

I have read up on few SO articles as well as tutorials but every tutorial ends with author saying that role based authorization should be handled on the server side and I understand why this is true.

It would be great if anyone can point me to tutorials or write-ups which has role-based authorization implemented on server side for AngularJS.

Thanks!

like image 916
CalmWinds Avatar asked Oct 27 '15 17:10

CalmWinds


People also ask

How role-based authentication works in Angular?

The auth guard uses the authentication service to visualize if the user is logged in, if they're logged in, it checks if their role is permitted to access the requested route. If they're logged in and licensed the canActivate() method returns true, otherwise, it returns false and redirects the user to the login page.

How do you do role-based authorization?

Each group has a set of permissions. For role-based authorization, the customer is responsible for providing the user ID, any optional attributes, and all mandatory user attributes necessary to define the user to Payment Feature Services. The customer must also define the roles that are assigned to the user.

How do you implement authorization in angular?

Open command prompt and go to project root folder. Start the application. Create a new service, AuthService to authenticate the user. Open AuthService and include below code.


1 Answers

I use role based authorization on backend as well as on frontend. Since I am using UI-Router for routing, the best resource I found (and improved to my needs) is this article:

link expired

If you use UI Router, definitely check it out. Basically you need to set up your routes security and intercept all route changes. The article also includes a directive for hiding user interface elements, if the user doesn't have permission to access the content behind it.


Edit: Adding some code.

First, you need to have user's permissions stored somewhere, e.g. on user object serialized in localStorage:

{"id":1,"name":"user","created_at":"2016-04-17 18:58:19","gender":"m","roles":["admin"]}

Then, you have two important parts:

  • directive - to determine if element should be visible or not based on assigned permission
  • service - to handle authorization checking

Directive:

(function() {
  'use strict';

  angular
    .module('app')
    .directive('access', access);

  /** @ngInject */
  function access(authorization) {
    var directive = {
      restrict: 'A',
      link: linkFunc,
    };

    return directive;

    /** @ngInject */
    function linkFunc($scope, $element, $attrs) {
      var makeVisible = function () {
        $element.removeClass('hidden');
      };

      var makeHidden = function () {
        $element.addClass('hidden');
      };

      var determineVisibility = function (resetFirst) {
        var result;

        if (resetFirst) {
          makeVisible();
        }

        result = authorization.authorize(true, roles, $attrs.accessPermissionType);

        if (result === authorization.constants.authorised) {
          makeVisible();
        } else {
          makeHidden();
        }
      };

      var roles = $attrs.access.split(',');

      if (roles.length > 0) {
          determineVisibility(true);
      }
    }
  }

})();

You need to set your CSS so that elements with class hidden are not visible.

Service:

(function() {
  'use strict';

  angular
    .module('app')
    .factory('authorization', authorization);

  /** @ngInject */
  function authorization($rootScope) {
    var service = {
      authorize: authorize,
      constants: {
        authorised: 0,
        loginRequired: 1,
        notAuthorised: 2
      }
    };

    return service;

    function authorize(loginRequired, requiredPermissions, permissionCheckType) {
      var result = service.constants.authorised,
          user = $rootScope.currentUser,
          loweredPermissions = [],
          hasPermission = true,
          permission;

      permissionCheckType = permissionCheckType || 'atLeastOne';

      if (loginRequired === true && user === undefined) {
          result = service.constants.loginRequired;

      } else if ((loginRequired === true && user !== undefined) &&
                  (requiredPermissions === undefined || requiredPermissions.length === 0)) {
          result = service.constants.authorised;

      } else if (requiredPermissions) {

          loweredPermissions = [];

          angular.forEach(user.roles, function (permission) {
              loweredPermissions.push(permission.toLowerCase());
          });

          for (var i = 0; i < requiredPermissions.length; i += 1) {
              permission = requiredPermissions[i].toLowerCase();

              if (permissionCheckType === 'combinationRequired') {
                  hasPermission = hasPermission && loweredPermissions.indexOf(permission) > -1;
                  // if all the permissions are required and hasPermission is false there is no point carrying on
                  if (hasPermission === false) {
                      break;
                  }
              } else if (permissionCheckType === 'atLeastOne') {
                  hasPermission = loweredPermissions.indexOf(permission) > -1;
                  // if we only need one of the permissions and we have it there is no point carrying on
                  if (hasPermission) {
                      break;
                  }
              }
          }

          result = hasPermission ?
                   service.constants.authorised :
                   service.constants.notAuthorised;
      }

      return result;
    }
  }
})();

Now, you can use the directive to show/hide element:

<a ui-sref="app.administration" class="btn btn-primary pull-right" access="admin">Administration</a>

Of course this will only hide the element in DOM, so you must do the authorization check on the server too.

This first part solved to showing/hiding elements in user interface but you can also protect app routes.

Route definition:

(function() {
  'use strict';

  angular
    .module('app')
    .config(routeConfig);

  /** @ngInject */
  function routeConfig($stateProvider) {
    $stateProvider
      .state('app.dashboard', {
        url: '/dashboard',
        data: {
          access: {
            loginRequired: true
          }
        },
        templateUrl: 'template_path',
        controller: 'DashboardController as vm'
      }
  }
})();

and now just check for permission in $stateChangeStart event

(function() {
  'use strict';

  angular
    .module('app')
    .run(runBlock);

  /** @ngInject */
  function runBlock($rootScope, $state, authorization) {
    $rootScope.$on('$stateChangeStart', function(event, toState) {
      // route authorization check
      if (toState.data !== undefined && toState.data.access !== undefined) {
        authorised = authorization.authorize(toState.data.access.loginRequired,
                                             toState.data.access.requiredPermissions,
                                             toState.data.access.permissionCheckType);

        if (authorised === authorization.constants.loginRequired) {
          event.preventDefault();
          $state.go('app.login');
        } else if (authorised === authorization.constants.notAuthorised) {
          event.preventDefault();
          $state.go('app.dashboard');
        }
      }
    });
  }

})();
like image 62
Adrian Avatar answered Sep 19 '22 05:09

Adrian