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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With