Angular $http Interceptor Opens Multiple Modals When Auth Fails

664 views Asked by At

I am using the $http interceptor to show a login modal when $auth fails. Here is a brief set-up I have:

$provide.factory('modalWhenLoggedOut', ['$q', '$injector', '$rootScope',
          function($q, $injector, $rootScope) {

        return {

             responseError: function(rejection) {

                 // $injector.get to fix circular dependency error
                 var loginModal = $injector.get('LoginModalService');
                 var $http = $injector.get('$http');
                 var $state = $injector.get('$state');

                 // Check for reasons instead of status code
                 var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];

                 // Loop through each rejection reason and redirect
                 angular.forEach(rejectionReasons, function(value, key) {

                     if(rejection.data.error === value) {
                        // Show the login modal
                         var deferred = $q.defer();

                         loginModal()
                           .then(function () {
                             deferred.resolve( $http(rejection.config) );
                         })
                         .catch(function () {
                             $state.go('index');
                             deferred.reject(rejection);
                        });

                        return deferred.promise;
                     }
                 });

                 return $q.reject(rejection);
             }
         }
}]);

// Push the new factory onto the $http interceptor array
$httpProvider.interceptors.push('modalWhenLoggedOut');

The above code works fine. But the problem I have is when the api throws multiple auth failure error, the interceptor opens up multiple modals at the same time. Usually in 1 page I might have 2-3 times various services contacting the backend api and when the auth fails, all of those api returns auth failure error during invalid tokens. This interceptor is picking it up as 3 taken_invalid errors and showing 3 login modals 1 on top of each other. How do I prevent this?

How can I make sure the interceptor only shows 1 login modal no matter how many times the auth failure occurred?

My Service Looks something like this:

.service('LoginModalService', ['$modal', '$rootScope', 
            function($modal, $rootScope) {

                    function updateRootScope() {
                            $rootScope.loginProgress = false;
                    }

                  if ($rootScope.loginProgress = false) {
                    return function() {
                      var instance = $modal.open({
                        templateUrl: rootPath + 'views/partials/LoginModalTemplate.html',
                        controller: 'LoginModalCtrl'
                      })

                    return instance.result.then(updateRootScope);
                  };
                  }
}])

In the service I am trying to use $rootScope.loginProgress as a switch to figure out if a login modal is opened or not. However, I am not sure how to return an empty promise if modal is already opened (when $rootScope.loginProgress is true).

1

There are 1 answers

0
Neel On BEST ANSWER

To overcome my issue, I am now using http-auth-interceptor interceptor and then changing the $rootScope.loginProgress based on the login modal open status. The consecutive modals that are opened due to multiple 401s will check for this login modal status and then decides to open a modal or not. Using http-auth-interceptor, I am able to add the switch by watching the 'event:auth-loginRequired'. Here is the code I am using:

scope.$on('event:auth-loginRequired', function() {      
            if(!$rootScope.loginProgress) {
                        $rootScope.loginProgress = true;
                        LoginModalService()
                            .then(function () {
                              authService.loginConfirmed();
                                    $rootScope.loginProgress = false;
                          })
                          .catch(function () {
                              authService.loginCancelled();
                                    $rootScope.loginProgress = false;
                          });
            }
});

Where LoginModalService() is the service that opens the login modal.