Circular dependency found: $http <- $templateFactory <- $view <- $state - angularjs

I have a current 401 check that I'm running with $location which is working fine. However I'd like to swap it over to $state and use ui-router instead. When I do so i get an error code as:
Circular dependency found: $http <- $templateFactory <- $view <- $state <- authHttpResponseInterceptor <- $http <- $compile
My currently code looks fine as I check certain paths and allow no logged in users to view them:
/* Look for 401 auth errors and then redirect */
.factory('authHttpResponseInterceptor',['$q','$location', function($q,$location) {
return {
response: function(response){
if (response.status === 401) {
}
return response || $q.when(response);
},
responseError: function(rejection) {
var reservedPaths = ['/','/login','/connect','/event'];
if (rejection.status === 401 && _.contains(reservedPaths,$location.path().trim())) {
$location.path('/welcome');
}
return $q.reject(rejection);
}
};
}])
.config(['$httpProvider',function($httpProvider) {
//Http Intercpetor to check auth failures for xhr requests
$httpProvider.interceptors.push('authHttpResponseInterceptor');
}]);
The code I added is as follows.:
/* Look for 401 auth errors and then redirect */
.factory('authHttpResponseInterceptor',['$q','$location', **'$state',** function($q,$location, **$state**) {
return {
response: function(response){
if (response.status === 401) {
}
return response || $q.when(response);
},
responseError: function(rejection) {
var reservedPaths = ['/','/mycube','/connect','/event'];
if (rejection.status === 401 && _.contains(reservedPaths,$location.path().trim())) {
**$state.go('home');**
}
return $q.reject(rejection);
}
};
}])
.config(['$httpProvider',function($httpProvider) {
//Http Intercpetor to check auth failures for xhr requests
$httpProvider.interceptors.push('authHttpResponseInterceptor');
}]);
Why would adding state cause this issue when it works fine with location?

It appears that $state service is resulting in a circular dependency with the $http service. This may be caused by the fact that the templateFactory (see https://github.com/angular-ui/ui-router/blob/master/src/templateFactory.js) is being injected with the $http service in addition to the interceptor itself being composed with the $http service.
To get around this circular dependency issue, you can use the $injector service to wire up the $state service to your interceptor. See the revised code:
/* Look for 401 auth errors and then redirect */
module.factory('authHttpResponseInterceptor', ['$q','$location', '$injector', function($q, $location, $injector) {
return {
response: function(response){
if (response.status === 401) {
}
return response || $q.when(response);
},
responseError: function(rejection) {
var reservedPaths = ['/', '/mycube', '/connect', '/event'];
if (rejection.status === 401 && _.contains(reservedPaths, $location.path().trim())) {
var stateService = $injector.get('$state');
stateService.go('home');
}
return $q.reject(rejection);
}
};
}]);
You can learn more about the $injector service here: https://docs.angularjs.org/api/auto/service/$injector
IMPORTANT
I would recommend using the state change events (see https://github.com/angular-ui/ui-router/wiki#state-change-events) to watch for errors using $stateChangeError and inspecting the error returned from the 401.

Here is the simplest solution I did and it worked. Inside the factory write:
var $http = $injector.get("$http");
and then use $http as you normally would.
NOTE: If you don't have $injector available in your factory simply inject it as follow
.factory('authHttpResponseInterceptor',['$q','$location','$injector', function($q,$location,$injector) {
}])

Related

$http interceptor is causing an infinite loop

I'm building an AngularJs app working with a custom API, the latter needs a valid authorization token, if it isn't, my API returns a 4O1 HTTP status code.
Thus, I use a http interceptor which is meant to ask for a new token before retrying the previous request.
app.factory('httpResponseErrorInterceptor', ['$q', '$injector', function($q, $injector, $http) {
return {
'responseError': function(response) {
if (response.status === 401) {
// should retry
let deferred = $q.defer();
let $http = $injector.get('$http');
$.ajax({
url : PUBLIC_API_URI,
type : 'HEAD',
beforeSend : function(request) {
request.setRequestHeader('Authorization', api_access_key);
},
success : function(result, status, xhr) {
console.log("error -> ok : should retry");
deferred.resolve(xhr.getResponseHeader('Authorization'));
},
error : function(xhr, status, error) {
console.log("error -> bad : should give up");
deferred.resolve(null);
}
});
console.log(deferred.promise);
if(deferred.promise == null)
return $q.reject(response);
response.config['Authorization'] = api_access_key = deferred.promise;
return $http(response.config);
}
return $q.reject(response);
}
};
}]);
I used JQuery.ajax in my interceptor because I guessed that using $http was causing the infinite loop when the token-renewal request resulted in an error.
But it still causes an infinite loop on error :
original-request -> renewal-request ...
i don't see the complete log of the error but i guess the problem could be related to the injection of $http service (that is still there even if you're using $.ajax) inside an http interceptor. this will cause a circular dependency because angular will face an infinite loop trying to resolve both the dependency of $http and its interceptor.
see my previous answer here
if you need to make an ajax call try injecting the service "by need"
angular.module('myApp').factory('interceptor', function($injector){
return {
'responseError': function(response) {
var http = $injector.get('$http');
http.get..
}
}
});

Circular dependency found: Http Response Interceptor 401

The basic idea was to use an Http Response Interceptor to redirect my webpage if it gives an 401 status. But i don't know if i am doing this the right way: i thought it was something like this but i seems more difficult than it seems. At the moment i get an Circular dependency found.
Do i need to push the interceptor somewhere else? And how can the interceptor know if i get an 401 request. is it also possible to define which 401 needs to be intercept and which ones ignored
(function () {
'use strict';
angular
.module('app', ['ngRoute','ngCookies','ngMessages'])
.config(routeConfig);
routeConfig.$inject = ['$routeProvider','$httpProvider'];
function routeConfig($routeProvider,$httpProvider) {
$routeProvider
.when('/', {
templateUrl: 'login.html',
controller : 'LoginController'
})
.when('/register', {
templateUrl: 'register.html',
controller : 'RegisterController'
})
.when('/main', {
//This gives an 401 status when the user has not been logged in
templateUrl: 'home.html',
controller : 'HomeController'
})
.otherwise('/');
// We need to add this for csrf protection
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
//this gives an Circular dependency error
$httpProvider.interceptors.push('HttpResponseInterceptor');
}
})();
This is my service:
(function () {
'use strict';
angular
.module('app')
.factory('HttpResponseInterceptor', HttpResponseInterceptor);
HttpResponseInterceptor.$inject = ['$rootScope','$http','$q','$location'];
function HttpResponseInterceptor($rootScope,$http,$q,$location) {
return {
response: function(response){
if (response.status === 401) {
}
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
};
}
})();
Update2
As mention in the comment i was injecting to much stuff. So this is one problem fixed, but now when i go to the login page it makes a request on loading the page (localhost:8080/user) which results in an infinite loop of redirection to the login page and with a browser crash as result.
So is there a way i can say to the Interceptor which url's needed to be redirected and which ones don't
This is answer to 2nd problem ...
You query the rejection object and add some further conditions to your IF statement ...
you can dump rejection object with ...
console.log(JSON.stringify(rejection));
then add conditions to your IF ...
if (rejection.status === 401 && rejection.config.url !== "/url/you/do/not/want/to/change/state/on") {
// do state.go or something else here
}
does injecting services using $injector help?
(function () {
'use strict';
angular
.module('admin')
.factory('HttpInterceptor', httpInterceptor);
httpInterceptor.$inject = [
'$q', // return promises
'$injector' // login service injection
];
function httpInterceptor(q, injector) {
return {
responseError: function (rejection) {
var url = rejection.config ? rejection.config.url : undefined;
var state = injector.get("$state");
if (rejection.status === 401) {
if (!(url === "/api/logout" || state.current.name === "login" && url === "/api/authenticated")) {
var loginService = injector.get('LoginService');
loginService.logout();
}
}
if (rejection.status === 403) {
state.go("home");
}
return q.reject(rejection);
}
};
}
}());

Angular ngResources request interceptor

Why angular $resources not have a request and request error interceptor?
Theres any way to do that?
Doc content:
interceptor - {Object=} - The interceptor object has two optional methods - response and responseError. Both response and responseError interceptors get called with http response object. See $http interceptors.
You can implement your own interceptors as follows.
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
});
app.factory('myInterceptor', ['$q', function ($q) {
return {
request: function (config) {
config.headers = config.headers || {};
// insert code to populate your request header for instance
return config;
},
response: function (response) {
if (response.status === 403 || response.status === 401) {
// insert code to redirect to custom unauthorized page
}
return response || $q.when(response);
}
};
}]);
I hope this will help you out.

How to configure the $http service in Angular depending on itself?

I'm trying to configure the $http service of Angular, to redirect to an URL when the status code is 403.
No problems so far but the URL to redirect to is coming from the server, through a service which is using $http (obiously).
Here's a piece of code:
angular
.module('app')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', 'URLs',
function($q, Redirect) {
return {
request: function(config) {
return config || $q.when(config);
},
responseError: function(response) {
if(response.status === 403) {
// redirect to URLs.login
}
return $q.reject(response);
}
};
}
]);
}])
.factory('URLs', ['$http', function($http) {
var URLs;
$http.get('/urls').then(function(response) {
URLs = response.data;
});
return URLs;
}]);
This code is creating a circular dependency (error) in Angular.
Is there a way that I can do this, having dynamic URLs that are coming from a server and based on this to redirect the user to one of them when the response.status is 403?
Use $injector service to lazily load the URLs service:
angular
.module('app')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', '$injector',
function($q, $injector) {
return {
request: function(config) {
return config || $q.when(config);
},
responseError: function(response) {
var Redirect = $injector.get('URLs');
if(response.status === 403) {
// redirect to URLs.login
}
return $q.reject(response);
}
};
}
]);
}])
You can also break this circular dependency in the URLs service by injecting the $injector there.

Capture HTTP 401 with Angular.js interceptor

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.
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');
}])
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);
}
}];
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);
}
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.

Resources