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
Turns out that all I needed to do was move the loading of config data to the .run()
block instead of trying to do it in the parent app
state's resolve
.
//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', 'localStorageService',
function ($rootScope, AUTH_EVENTS, SessionService, localStorageService)
{
$rootScope.$on('$stateChangeStart', function (event, next) {
//function to check to see if the currentUser has one of the required roles to authorize the next state.
var checkAuthorization = function(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");
}
}
};
//Before calling checkAuthorization(), test to see if the state change was triggered by a reload
//If so, load config data before triggering the `checkAuthorization()` function.
if (SessionService.freshLoad === true || typeof SessionService.freshLoad === 'undefined'){
SessionService.freshLoad = false;
var storedUser = localStorageService.get('currentUser');
//If we have a stored user but no existing session, then we know that we have stored
//user data to reload before the checkAuthorization() function.
if (typeof storedUser !== "undefined" && storedUser !== null && !SessionService.existingSession()) {
SessionService.restoreSession();
}
}
checkAuthorization(next.data.authorizedRoles);
});
}]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With