47

I'd like to implement authentication on a single page web app with Angular.js. The official Angular documentation recommends the using of interceptors:

$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
  return {

    // ...

    'responseError': function(rejection) {
      // do something on error
      if (canRecover(rejection)) {
        return responseOrNewPromise
      }
      return $q.reject(rejection);
    }
  };
});

The problem is when the server sends 401 error, the browser immediately stops with "Unauthorized" message, or with login pop-up window (when authentication HTTP header is sent by the server), but Angular can't capture with it's interceptor the HTTP error to handle, as recommended. Am I misunderstanding something? I tried more examples found on web (this, this and this for example), but none of them worked.

Community
  • 1
  • 1
Csati
  • 1,251
  • 1
  • 15
  • 28
  • Did you ever get this figured out? I'm having the exact same issue – Dave Chenell Sep 25 '14 at 07:16
  • Same here, none of the answer works, the interceptor does work, but the exception still throws before the interceptor. Is this weird ? – windmaomao Jan 06 '15 at 23:08
  • I have tried so many things, I still cannot catch a 401. Where you able to do it with just an interceptor? (for some reason, it gets translated to a 404) @windmaomao – natdico Jan 09 '15 at 21:52

4 Answers4

64

For AngularJS >1.3 use $httpProvider.interceptors.push('myHttpInterceptor');

.service('authInterceptor', function($q) {
    var service = this;

    service.responseError = function(response) {
        if (response.status == 401){
            window.location = "/login";
        }
        return $q.reject(response);
    };
})
.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('authInterceptor');
}])
Igor S.
  • 3,332
  • 1
  • 25
  • 33
  • 1
    'responseError' is the thing to do. For good measure, note that there is also 'requestError'. – eflat Dec 09 '16 at 22:42
  • wonderful answer! its a generic functionality, once you unauthorized it'll redirect to login. – sebu Jul 24 '17 at 05:55
  • Finally, this works!!! I was originally using "response" instead of "responseError" which was causing my interceptor functionality to not be executed. – mdlars Mar 13 '18 at 12:36
29

in app config block:

var interceptor = ['$rootScope', '$q', "Base64", function(scope, $q, Base64) {
  function success(response) {
    return response;
  }

  function error(response) {
    var status = response.status;
    if (status == 401) {
      //AuthFactory.clearUser();
      window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);
      return;
    }
    // otherwise
    return $q.reject(response);
  }
  return function(promise) {
    return promise.then(success, error);
  }
}];
r0m4n
  • 3,474
  • 3
  • 34
  • 43
Gecko
  • 1,333
  • 1
  • 14
  • 26
8

I don't know why, but response with 401 error goes into success function.

'responseError': function(rejection)
                {
                    // do something on error

                    if (rejection.status == 401)
                    {
                        $rootScope.signOut();
                    }

                    return $q.reject(rejection);
                },
                'response': function (response) {
                    // do something on error
                    if (response.status == 401) {
                        $rootScope.signOut();
                    };
                    return response || $q.when(response);
                }
vikdet
  • 89
  • 1
  • 1
  • 1
    'responseError' was very helpful :-) – joy Oct 11 '15 at 21:25
  • 1
    I also have found 401s ending up in `response` rather than `responseError`. Angular 1.5.3. EDIT: Looks like it may be due to a 3rd party lib I'm using, `http-auth-interceptor`. even though I've specified this particular request should be ignored by that lib, it seems it's still causing the 401 to not pipe through `responseError`. – elwyn Jun 08 '16 at 02:23
7

AngularJS interceptors only work for calls made with the $http service; if you navigate to a page that returns a 401, AngularJS never even runs.

ejain
  • 3,456
  • 2
  • 34
  • 52
  • 5
    This depends on what you call navigation. A full load of a page (window.location) will indeed not trigger the interceptor. But a navigation to another route in the app will, because the route templates are fetched using the $http service. – Gecko Aug 21 '15 at 06:29
  • In that case you can use: .$on("$locationChangeStart", function (event, next, current) { And .$on("$locationChangeSuccess", function (event, next, current) { – Chen Nov 28 '16 at 15:27
  • 1
    If you make a $http request to a server and returns 401, Angular has nothing to do with it, and developer should decide what to do; In the case of this question, he might want to redirect the user to a new route or even maybe remove auth token on both sides. – Mohammad Kermani Apr 29 '17 at 11:00