Lambo Jayapalan
Lambo Jayapalan

Reputation: 330

Reset Angular Service


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

Answers (1)

Kristján
Kristján

Reputation: 18803

I can think of a few options, varyingly reasonable.

  1. 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.

  2. 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.

  3. 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

Related Questions