Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite loop in interceptor

I've made an AngularJS website working with an API. This API provides few features like authentication (Oauth).

When the API returns a 401 error, it means that the access_token has expired and it needs to be refreshed with the refresh_token.

I created an interceptor in AngularJS. Its goal is to check if the result returned by the API is a 401 error and if it's the case, it must refresh the token and then, process the previous rejected request.

The problem is that the interceptor creates an infinite loop. After the second failure of the initial request, it should stop but it doesn't.

angular.module('myApp')
.factory('authInterceptor', function ($rootScope, $q, $window, $injector) {

  return {

    // If the API returns an error
    'responseError' : function(rejection) {

      // If it's a 401
      if (rejection.status == 401) {

        var deferred = $q.defer();

        $injector.get('$http').post('http://my-api.local/api/oauth/token', {
          grant_type    : 'refresh_token',
          client_id     : 'id',
          client_secret : 'secret',
          refresh_token : $window.sessionStorage.refresh_token
        }, {
          headers : {
            'Content-Type'  : 'application/x-www-form-urlencoded'
          },
          transformRequest  : function(obj) {
            var str = [];
            for(var p in obj)
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            return str.join("&");
          }
        })
        // New token request successfull
        .success(function(refreshResponse) {

          // Processing the failed request again (should fail again because I didn't saved the new tokens)
          $injector.get('$http')(rejection.config).success(function(data) {

            deferred.resolve(data);

          })
          .error(function(error, status) {

            deferred.reject();

          });

          return deferred.promise();

        })
        // New token request failure
        .error(function(error, status) {

          deferred.reject();
          // $location.path('users/login');

          return;

        });

      }
      // If it's another errorenter code here
      else
        return rejection;

    }

  }

});

So this code:

  • Starts when the first request fails
  • Refreshes the token
  • Retries the request but fails again (<- I just want to make it stop here)
  • Refreshes the token
  • Retries the request but fails again
  • Refreshes the token
  • Retries the request but fails again
  • etc...
like image 249
Flobesst Avatar asked Jun 02 '15 22:06

Flobesst


1 Answers

I worked on this in my app. Your refresh request needs to include a config/header variable like skipIntercept: true. Then when you intercept this as a failed response, you can check rejection.config.skipIntercept variable. If it is true, you go straight to $q.reject(rejection).

Where you have:

if (rejection.status == 401) {

Change it to:

if (rejection.status == 401 && !rejection.config.skipIntercept) {

And then above this:

     headers : {
        'Content-Type'  : 'application/x-www-form-urlencoded'
     },

You need to add:

     skipIntercept: true,

     headers: {
        'Content-Type'  : 'application/x-www-form-urlencoded'
     },

PS. there's an existing solution you can use.

like image 127
ngDeveloper Avatar answered Oct 24 '22 00:10

ngDeveloper