VishwaKumar
VishwaKumar

Reputation: 3433

Authorization Service fails on page refresh in angularjs

Implemented the authorization using the POST The problem is when i go to a privileged page say '/admin' it works but when i refresh the page manually, the admin page is redirecting to the '/unauthorized' page

Permissions service

angular.module('myApp')
    .factory('PermissionsService', function ($rootScope,$http,CookieService) {
        var permissionList;
        return {
            setPermissions: function(permissions) {
                permissionList = permissions;
                $rootScope.$broadcast('permissionsChanged')
            },
            getPermissions: function() {
                var roleId = 5
                if(CookieService.getLoginStatus())
                    var roleId = CookieService.getUserData().ROLE_ID;

                return $http.post('api/user-permissions', roleId).then(function(result){
                    return result.data;
                });
            },
            hasPermission: function (permission) {
                permission = permission.trim();
                return _.some(permissionList, function(item) {
                    if(_.isString(item.name))
                        return item.name.trim() === permission
                });
            }
        };
    });

hasPermissions directive

angular.module('myApp')
    .directive('hasPermission', function(PermissionsService) {
        return {
            link: function(scope, element, attrs) {
                if(!_.isString(attrs.hasPermission))
                    throw "hasPermission value must be a string";

                var value = attrs.hasPermission.trim();
                var notPermissionFlag = value[0] === '!';
                if(notPermissionFlag) {
                    value = value.slice(1).trim();
                }

                function toggleVisibilityBasedOnPermission() {
                    var hasPermission = PermissionsService.hasPermission(value);

                    if(hasPermission && !notPermissionFlag || !hasPermission && notPermissionFlag)
                        element.show();
                    else
                        element.hide();
                }
                toggleVisibilityBasedOnPermission();
                scope.$on('permissionsChanged', toggleVisibilityBasedOnPermission);
            }
        };
    });

app.js

var myApp = angular.module('myApp',['ngRoute','ngCookies']);

myApp.config(function ($routeProvider,$httpProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'app/module/public/index.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html'
        })
        .when('/login', {
            templateUrl: 'app/module/login/login.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html'
        })
        .when('/home', {
            templateUrl: 'app/module/home/home.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html'
        })
        .when('/register', {
            templateUrl: 'app/module/register/register.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html'
        })
        .when('/admin', {
            templateUrl: 'app/module/admin/admin.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html',
            permission: 'admin'
        })
        .when('/unauthorized', {
            templateUrl: 'app/partials/unauthorized.html',
            header: 'app/partials/header.html',
            footer: 'app/partials/footer.html'
        })
        .otherwise({redirectTo: '/'});

    $httpProvider.responseInterceptors.push('securityInterceptor');
});

myApp.provider('securityInterceptor', function() {
    this.$get = function($location, $q) {
        return function(promise) {
            return promise.then(null, function(response) {
                if(response.status === 403 || response.status === 401) {
                    $location.path('/unauthorized');
                }
                return $q.reject(response);
            });
        };
    };
});

myApp.run(function($rootScope, $location, $window, $route, $cookieStore, CookieService, PermissionsService) {
    PermissionsService.getPermissions().then(function(permissionList){
        PermissionsService.setPermissions(permissionList);
    });

    // Check login status on route change start
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
        if(!CookieService.getLoginStatus() && $location.path() != '/register' && $location.path() != '/login') {
            $location.path("/");
            $rootScope.$broadcast('notloggedin');
        }

        if(CookieService.getLoginStatus() && $location.path() == '/login') {
            $location.path("/home");
        }

        var permission = next.$$route.permission;
        if(_.isString(permission) && !PermissionsService.hasPermission(permission))
            $location.path('/unauthorized');

    });

    // Adds Header and Footer on route change success
    $rootScope.$on('$routeChangeSuccess', function (ev, current, prev) {
        $rootScope.flexyLayout = function(partialName) { return current.$$route[partialName] };
    });
});

CookieService

angular.module('myApp')
    .factory('CookieService', function ($rootScope,$http,$cookieStore) {
        var cookie = {
            data: {
                login: false,
                user: undefined
            },
            saveLoginData: function(user) {
                cookie.data.login = true;
                cookie.data.user = user;
                $cookieStore.put('__iQngcon',cookie.data);
            },
            deleteLoginData: function() {
                cookie.data.login = false;
                 cookie.data.user = undefined;
                 $cookieStore.put('__iQngcon',cookie.data);
            },
            getLoginStatus: function() {
                if($cookieStore.get('__iQngcon') === undefined)
                    return cookie.data.login;

                return $cookieStore.get('__iQngcon').login;
            },
            getUserData: function() {
                return $cookieStore.get('__iQngcon').user;
            }
        };

        return cookie;
    });

It seems like the permissions data are lost on page refresh. Is there any other way i can solve the problem? Or is there any problem with the code??

Upvotes: 1

Views: 2283

Answers (2)

VishwaKumar
VishwaKumar

Reputation: 3433

Well had to think for some time and made the following change for it to work. It may not be the best practice but ya worked for me. Would appreciate if anyone suggested me a better solution in the comments if found.

myApp.run(function($rootScope, $location, $window, $route, $cookieStore, CookieService, PermissionsService) {
    var permChanged = false;

    PermissionsService.getPermissions().then(function(permissionList){
        PermissionsService.setPermissions(permissionList);
    });

    // Check login status on route change start
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
        console.log('$routeChangeStart');
        if(!CookieService.getLoginStatus() && $location.path() != '/register' && $location.path() != '/login') {
            $location.path("/");
            $rootScope.$broadcast('notloggedin');
        }

        if(CookieService.getLoginStatus() && $location.path() == '/login') {
            $location.path("/home");
        }

        $rootScope.$on('permissionsChanged', function (ev, current, prev) {
            permChanged = true;
        });

        if(CookieService.getLoginStatus() && permChanged) {
            var permission = next.$$route.permission;
            if(_.isString(permission) && !PermissionsService.hasPermission(permission))
                $location.path('/unauthorized');
        }

    });

    // Adds Header and Footer on route change success
    $rootScope.$on('$routeChangeSuccess', function (ev, current, prev) {
        $rootScope.flexyLayout = function(partialName) { return current.$$route[partialName] };
    });
});

What i did was wait for the permissions to be set and then use the permissionChanged broadcast to set a permChanged variable to true and then combined with if user loggedin status and permchanged had to check the permissions if had

$rootScope.$on('permissionsChanged', function (ev, current, prev) {
                permChanged = true;
            });

            if(CookieService.getLoginStatus() && permChanged) {
                var permission = next.$$route.permission;
                if(_.isString(permission) && !PermissionsService.hasPermission(permission))
                    $location.path('/unauthorized');
            }

Upvotes: 0

JeffryHouser
JeffryHouser

Reputation: 39408

when i refresh the page manually, the admin page is redirecting to the '/unauthorized' page

Isn't that expected behavior? If you reload the page; then all UI state is lost; it is just like shutting down the app and starting from scratch.

It seems like the permissions data are lost on page refresh. Is there any other way i can solve the problem? Or is there any problem with the code??

If you want to be able to retain UI state after a page reload, you'll have to retain the Login information somehow, such as in a browser cookies. When the app loads; check for that cookie value. If it exists, you can load the user info from the database, essentially mirroring a login.

I'd be cautious about storing actual user credentials in a cookie without some type of encryption. One approach I've used is to store a unique user key which can be sent to the DB to load user info. Sometimes this may be a UUID associated with the user, Avoid using an auto-incrementing primary key because that is easy to change to get access to a different user's account.

Upvotes: 3

Related Questions