so I have this function in my run
in angularjs (running AngularJS v1.2.26)
.run(function(....) {
authService.authCheck();
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (next.requireLogin) {
if (!authService.isLoggedIn()) {
$location.path('/login');
event.preventDefault();
}
}
});
})...
when directly accessing a URL that requires authentication e.g. http://127.0.0.1:8080/secret
, the function always redirects to the login path, clearly after debugging it for a while it evaluates to false all the time. when accessing default page then going to the specified route it runs without any problems.
This is part of my authService
this.authCheck = function() {
$http({
method: 'PUT',
url: '/api/login'
}).
success(function(data, status, headers, config) {
if (status === 200) {
userIsAuthenticated = true;
} else {
userIsAuthenticated = false;
}
$rootScope.loginStatus = userIsAuthenticated;
}).
error(function(data, status, headers, config) {
userIsAuthenticated = false;
$rootScope.loginStatus = userIsAuthenticated;
});
};
this.isLoggedIn = function() {
return userIsAuthenticated;
};
So every time I have a break point on !authService.isLoggedIn()
I see that it evaluates to false even though the function in the run block tries to authenticate it before reaching the $rootScope.$on
function.
What exactly is preventing the state of userIsAuthenticated
variable from being persistent?
The .authCheck
in your authService
is asynchronous, which means that userIsAuthenticated
won't be set to true until it actually finishes for the first time. If you followed the route you're on now, you'd have to write some more complex code for waiting if the check isn't completed yet and then redirecting. However, that's a messy sub-optimal option.
The best way to go here is to let ui-router
work this out for you. Create a common ancestor state for all your states that require login, e.g. 'session'
. Then make that state's resolution wait for your authentication check using resolve
option. If not authenticated, create a specific state transfer rejection error, which you can elsewhere catch and act upon (redirect to login).
.state('session', {
resolve: {
authentication: ['authService', '$q', function (authService, $q) {
return authService.checkAuthentication().then(function () {
if (!authService.isAuthenticated) {
return $q.reject({
notAuthenticated: true
});
}
});
}]
},
// ...
})
.state('secret', {
parent: 'session',
// ...
});
and then
$rootScope.$on('$stateChangeError', function (_0, _1, _2, _3, _4, error) {
if (error.notAuthenticated) {
$state.go('login');
}
});
(you'll need to properly return from your authentication check as well)
checkAuthentication: function () {
return $http({...})
.success(...)
.error(...);
}