Reputation: 330
Is there a good way to reset the data in a factory/service in angular without creating a dependency to it?
I currently have an AuthService that takes a username and password, and gets an oauth token from a server. I also have an http interceptor that adds the token to all requests.
If I get a 401 (unauthorized) response then my token is no longer valid and I want to set _AuthData inside AuthService to null. But I have no good way of doing that right now.
If I add an AuthService dependancy into the interceptor (to be able to call LogOut()) then I get a circular reference since AuthService uses $http.
I keep re-reading the token from the localstorageservice inside AuthService for methods like IsAuthenticated() and Username(), but I'd like to avoid that if possible to avoid the performance hit.
Is there a way to "reset" AuthService from the AuthInterceptorService without creating a dependancy?
AuthService
appRoot.factory("AuthService", ["$http", "$q", "localStorageService", function ($http, $q, localStorageService) {
var _AuthData;
var AuthServiceFactory = {};
AuthServiceFactory.Username = function () {
return _AuthData.Username;
};
AuthServiceFactory.Roles = function () {
return _AuthData.Roles;
};
AuthServiceFactory.IsAuthenticated = function () {
return _AuthData != null;
};
AuthServiceFactory.LogOut = function () {
_AuthData = null;
localStorageService.remove("AuthData");
};
AuthServiceFactory.Login = function (Username, Password) {
var Deferred = $q.defer();
$http.post(ApiBaseUrl + "token", Username, { headers: { 'Content-Type': "application/x-www-form-urlencoded" } }).success(function (Response) {
_AuthData = {
Token: Response.access_token,
Username: Username,
Roles: Response.Roles
};
localStorageService.set("AuthData", _AuthData);
Deferred.resolve(Response);
}).error(function (err, status) {
Deferred.reject(err);
});
return Deferred.promise;
};
return AuthServiceFactory;
}]);
AuthInterceptorService
appRoot.factory("AuthInterceptorService", ["$q", "$location", "localStorageService", function ($q, $location, localStorageService) {
var AuthInterceptorServiceFactory = {};
AuthInterceptorServiceFactory.request = function (config) {
config.headers = config.headers || {};
var AuthData = localStorageService.get("AuthData");
if (AuthData) {
config.headers.Authorization = "Bearer " + AuthData.Token;
}
return config;
};
AuthInterceptorServiceFactory.responseError = function (Rejection) {
if (Rejection.status === 401) {
localStorageService.remove("AuthData");
//AuthService.LogOut(); //Need to reset token here
$location.url("/Login");
}
return $q.reject(Rejection);
};
return AuthInterceptorServiceFactory;
}]);
Upvotes: 2
Views: 2367
Reputation: 18803
I can think of a few options, varyingly reasonable.
Just the first thing to consider - is the performance hit from local storage really an issue for you? Your current solution is straightforward and easy to understand, and that's a feature in itself.
Split AuthService
into an Authorizer
and AuthStorage
. That way Authorizer
can depend on $http
, AuthStorage
doesn't need to, and AuthInterceptorService
can then depend on AuthStorage
, where you can put the reset function.
This one feels like a big hammer, but AuthInterceptorService
can broadcast an auth_failed
event on appRoot
, which AuthService
can listen for to perform the reset. That's heading towards pretty global message passing, so I'd be concerned about its maintainability.
Upvotes: 1