I have an angular app that uses authorization logic and ui-router to bar unauthorized users for certain states/views. I follow the standard approach of listening for a stateChange event, which triggers my authorization logic. This all works well until the dreaded page re-load.
I store session data (including authorization status) in local storage so that on page reloads I can use a parent state in ui-router to first resolve/get the authorization status from local storage prior to attempting to change views. Here is the configuration of my app
parent state object:
$stateProvider.
state('app', {
url: '/app',
abstract: true,
controller: 'appCtrl',
data: {
authorizedRoles: [USER_ROLES.all]
},
templateUrl: 'partials/app.html',
resolve: {
//Try to restore from the previous session before loading any of the app child states
RestoredSession: ['SessionService',
function(SessionService){
return SessionService.restoreSession();
}]
}
})
...various app. child states
And here is my onStateChange listener:
//listen for a ui.router $stateChangeStart event and test the new path to see if the currentUser
//is authorized to view that page
.run( ['$rootScope', 'AUTH_EVENTS', 'SessionService',
function ($rootScope, AUTH_EVENTS, SessionService) {
$rootScope.$on('$stateChangeStart', function (event, next) {
var authorizedRoles = next.data.authorizedRoles;
//If the requested page allows guest access, then continue to stateChange
if (authorizedRoles.indexOf('guest') !== -1 || authorizedRoles.indexOf('*') !== -1) return;
//If the requested page requires authorization, check login and auth privileges
if (!SessionService.isAuthorized(authorizedRoles)) {
event.preventDefault();
if (SessionService.existingSession()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
console.log("User attempted to access page for which he is not authorized");
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notLoggedIn);
console.log("User attempted to access page when he is not logged in");
}
}
});
}]);
My problem is that the stateChangeStart
event is triggering prior to the app resolve
such that the listener stops the state change (via the event.preventDefault
), and then my resolve loads the stored session data, which often establishes that the user was authorized all along. If I could require execution of the resolve prior to the event triggering then I'd be golden.
Any ideas out there???
BTW, here is a similar SO question that went unanswered: Defer Angular UI Router $stateChangeStart until server authorization response receieved