I try to perform a http.post call in a http interceptor, but I get a
Circular dependency found: $http <- Ace <- authInterceptor <- $http <- $templateRequest <- $compile
I see why but I dont know how to solve it... Still new to angular and a little confusing sometime, I hope you can help me :) Heres is my code:
var app = angular.module('AceAngularApi', []);
app.service('Ace', ['$http', '$q', '$injector', '$window', function($http, $q, $injector, $window) {
var user = null;
var getCurrentUser = function() {
var url = "http://localhost:8080/api/currentuser";
var response = $http.post(url, {}).then(function(response) {});
return response;
};
return {
getCurrentUser: getCurrentUser,
}
}]);
app.factory('authInterceptor', ['$rootScope', '$q', '$window', '$injector', 'Ace',
function($rootScope, $q, $window, $injector, Ace) {
return {
request: function(config) {
config.headers = config.headers || {};
if ($window.localStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function(response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
} else {
Ace.getCurrentUser().then(function() {
console.log("Got current user");
});
}
return response || $q.when(response);
}
};
}
]);
app.config(function($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
You are trying to define $http's pre-processing functionality by injecting authInterceptor into $httpProvider, however authInterceptor has dependency on $http, it leads to circular dependency problem.
To get around this circular dependency issue, you can use the $injector service to wire up Ace
app.factory('authInterceptor', ['$rootScope', '$q', '$window', '$injector',
function($rootScope, $q, $window, $injector) {
return {
response: function(response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
} else {
var Ace = $injector.get('Ace');
Ace.getCurrentUser().then(function() {
console.log("Got current user");
});
}
return response || $q.when(response);
}
};
}
]);
Another workaround is registering the interceptor in a run() block instead of a config() block, but keep in mind, before run() is executed, any call to $http have nothing to do with authInterceptor
Related
I'm using Angular 1.6.4, Express 4.15.2 and express-session.
I am trying to catch if the user is unauthorized to access a certain route by checking the existence of req.session.user parameter. If he's not, I'd like to send a 401 response status and change the state in Angular.
The problem is that I am not getting any response object to check the status of.
I have tried using an interceptor, logging out error.response.body, logging out everything really to find out where it is that I'm losing the response object.
Here's some code, any help would be greatly appreciated!
express:
app.get('/update', sessionCheck, function(req, res) {
res.send('session');
});
function sessionCheck(req, res, next){
if(req.session.user) {
next();
} else {
console.log('before');
return res.status(401).send('Unauthorized');
console.log('after');
}
}
angular:
.state('update', {
url: '/update',
views: {
"": {
templateUrl: 'templates/update.html',
controller: function($http) {
return $http.get('/update').then(function(response) {
console.log('Ok response' + response);
}, function(error) {
console.log('Error response' + error.response.body);
});
},
},
"carousel": {
templateUrl: "templates/carousel.html"
},
"footer": {
templateUrl: "templates/footer.html"
}
}
})
network screen
Have you tried to do this using an interceptor?
You can try in this way:
anyModule.service('yourInterceptor', function($q) {
var service = this;
service.responseError = function(response) {
if (response.status == 401){
//do something
}
return $q.reject(response);
};
})
Note that here we are dealing with responseError.
You need to register you interceptor too in a config function:
$httpProvider.interceptors.push('yourInterceptor');
You can see this for more information about this interceptor:
Capture HTTP 401 with Angular.js interceptor
UPDATE:
You can register an interceptor in this way too:
app.factory("YourInterceptor", ["$q", "$rootScope", "$location",
function($q, $rootScope, $location) {
var success = function(response) {
//do something
return response;
},
error = function(response) {
if(response.status === 401) {
// do something
}
return $q.reject(response); //reject on error
};
return function(httpPromise) {
return httpPromise.then(success, error);
};
}
Then you register your interceptor in this way (in a config of the module):
$httpProvider.responseInterceptors.push("YourInterceptor");
Note that you push the interceptor in responseInterceptors. This work to me.
I have interceptor, however it all responses only goes into request(), even if 500 code returned
Here is my code
angular.module("BusinessTool").factory('InterceptorService',['$q', '$location', function( $q, $location, $http ){
resp = $.get('api/me.json');
return {
request: function(config){
// everything is ok
return config;
},
responseError: function(rejection) {
//error here. for example server respond with 401
if (resp.status == 401) {
window.location.href = '/';
}
return $q.reject(rejection);
}
}
}]);
angular.module("BusinessTool").config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('InterceptorService');
}]);
I want to redirect to / url in case of 401. Also is it possible to force interceptor to do request to api/me.json before each request?
I have a authentication code, when I get a success message as in 200 reponse status then its fine but if I get 401 the page should show error as I have set some error status but this does not happen and I get the view also refreshes and turns out to be blank. Please help
app.controller('MyController', ['$scope', '$location', '$http','$rootScope','$route','$localStorage', function ($scope, $location, $http, $rootScope,$route, $localStorage) {
$scope.username = "";
$scope.password = "";
$scope.ipsenData = {};
$scope.username="";
$scope.password="";
$scope.invalidUser="";
/*Functionalities of the Application*/
$scope.login = function () {
$http.post('/ipsen/login', {
username: $scope.username,
password: $scope.password
}).
success(function (data, status, headers, config) {
if(status===200){
console.log("Login Successful");
$http.get('/ipsen/getrole?userName='+$scope.username)
.success(function (data, status, headers, config) {
if(data.rows[0].RoleName==="Ipsen Manager"){
var data = data.rows[0];
/*Username and Role Storage*/
$localStorage.username=$scope.username;
$localStorage.FirstName=data.FirstName;
$localStorage.LastName=data.LastName;
$localStorage.OrganizationName=data.OrganizationName;
$localStorage.RoleName=data.RoleName;
$localStorage.Email=data.Email;
$location.path('/customers').replace();
}
else{
var data = data.rows[0];
/*Username and Role Storage*/
$localStorage.FirstName=data.FirstName;
$localStorage.LastName=data.LastName;
$localStorage.OrganizationName=data.OrganizationName;
$localStorage.RoleName=data.RoleName;
$localStorage.Email=data.Email;
$location.path('/furnacelist/'+$scope.username+"/"+"customer").replace();
}
/**/
})
.error(function (data, status, headers, config) {
console.log(data);
});
}
}).
error(function (data, status, headers, config) {
if(status === 401){
$scope.invalidUser="Please Enter Valid Username & Password";
}
});
};
}]);
You could set up an interceptor like the following, which may help so that on a 401 your user will simply be stuck on the login page.
function APIInterceptor($rootScope, $q) {
var service = this;
service.responseError = function(response) {
console.log("in responseError", response); // just to see the error initially
if (response.status === 401) {
$rootScope.$broadcast('unauthorized');
return $q.reject(response);
}
return response;
};
}
Then you put the interceptor in your configuration file that handles the routes.
$httpProvider.interceptors.push('APIInterceptor');
With the following code using rootScope.$on within .run.
$rootScope.$on('unauthorized', function() {
// ... some code to handle tokens, cookies, whatever ... set to null
$location.path('/login');
});
There are other bits I have left out, but I think you get the point. Basically, you want to add an interceptor in there to handle your requests and responses.
I copied the following code from the web to work with JWT authorisation, but it does not work. In particular the $location.path command has no impact - the redirect does not take place. I also tried with $state.go, but that led to bigger errors. I don't fully understand what $q is referring to here, not what is waiting for the promise to unwind, but the issue is $location.path not taking the user back to the login screen (nor are the proposals below with respect to $state changes).
$httpProvider.interceptors.push(function($q, $location, $localStorage) {
return {
'request': function (config) {
config.headers = config.headers || {};
if ($localStorage.token) {
config.headers.Authorization = 'Bearer ' + $localStorage.token;
}
return config;
},
'responseError': function(response) {
if(response.status === 401 || response.status === 403) {
console.log("app.js: httpInterceptor caught authorisation status response");
delete $localStorage.token;
$location.path('/'); // to login page
// $state.go('login');
}
return $q.reject(response);
}
};
});
}
To test, I send a message that creates an server error, catch that in devtools on the way back, and then manually set response.status = 403. The redirect is clearly parsed but does not lead to the redirect. I can see that the login screen is put back on the screen, but is then immediately overwritten by a different view.
This is the factory $http ajax request. Is it possible that the deferred $q I use here is interfering with that in the interceptor?
$http(httpObj)
.success(function (response) { // returns 0: mongo resto data, 1: wpserver report
console.log("%s succesful, response: %s", method, response);
if (!updateNotAdd) {
Restos.data.restos.push(response[0]); // add to local copy of data
} else {
// replace existing entry with new information
var idxToReplace = _.findIndex(Restos.data.restos, function(r) {
return r.qname === resto.qname;
});
// copy over all data from editor model to database
_.assign(Restos.data.restos[idxToReplace], resto);
}
var response = {
success: true,
id: response[0]._id,
message: response[1]
};
$rootScope.$broadcast("ajaxresponse", response);
deferred.resolve(response);
})
.error(function (msg) {
// console.log(msg);
var response = {
success: false,
message: msg
};
$rootScope.$broadcast("ajaxresponse", response);
deferred.resolve(response);
});
return deferred.promise;
You shouldn't have an issue leveraging ui-router here. You need to use the $injector service to get a reference on the $state service. Observe the following change...
$httpProvider.interceptors.push(function($q, $location, $localStorage, $injector) {
return {
'request': function (config) {
config.headers = config.headers || {};
if ($localStorage.token) {
config.headers.Authorization = 'Bearer ' + $localStorage.token;
}
return config;
},
'responseError': function(response) {
if(response.status === 401 || response.status === 403) {
console.log("app.js: httpInterceptor caught authorisation status response");
delete $localStorage.token;
$injector.get('$state').go('login');
}
return $q.reject(response);
}
};
});
}
The bigger issues you're experiencing are caused by a circular dependency due to ui-router injecting $http into $TemplateFactory - leading to a circular reference to $http inside $httpProvider when you attempt to inject $state (which doesn't yet appear to be in your interceptors injection signature anyways)
Not sure whether you want to use, but probably this would redirect:
$window.location.href = url;
I'm setting up authorization with AngularJS and angular ui router.
If a user tries to access a route which requires authorization, the server returns a 401 status code.
I have created an HTTP response interceptor which checks for the 401 status code.
If the code is 401, it is supposed to redirect to the log in page, but this does not work.
It catches the 401, but does not redirect. Here is the code for the interceptor:
app.config(['$httpProvider', function ($httpProvider) {
// This is just for to check for XHR requests on the server
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$httpProvider.responseInterceptors.push(['$q', '$location', function ($q, $location) {
return function (promise) {
return promise.then(
// Success: just return the response
function (response) {
return response;
},
// Error: check the error status to get only the 401
function (response) {
if (response.status === 401)
$location.url('/users/login');
return $q.reject(response);
}
);
}
}]);
}]);
EDIT: It seems to be working if I do it like this:
$timeout(function() { $location.url('/users/login'); }, 0);
I guess that puts it last in the execution stack and changes the execution context. Does anyone know more about this, or if this indeed does work or only seems so?
I add the same issue. So I changed my code to use the $location.path (not url)
$location.path("/users/login");
Can you try that
angular.module('yourApp').config(function ($httpProvider) {
var logsOutUserOn401 = ['$q', '$location', function ($q, $location) {
var success = function (response) {
return response;
};
var error = function (response) {
if (response.status === 401) {
//redirect them back to login page
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
};
return function (promise) {
return promise.then(success, error);
};
}];
$httpProvider.responseInterceptors.push(logsOutUserOn401);
});
(source : http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/)
I have it working with
$window.location.href='...'
I finally solved it, but I still don't know why it didn't work. Anyway, I had to use this to redirect: (state transition)
$injector.get('$state').transitionTo('users.login');
this is how we added a response interceptor in our project. its working for us.
$httpProvider.interceptors.push(function($q, $rootScope, $location,
$cookieStore) {
return {
'responseError' : function(
rejection) {
var status = rejection.status;
var config = rejection.config;
var method = config.method;
var url = config.url;
if (status == 401) {
$rootScope.shouldRedirect = true;
$rootScope.urlAttempted = url;
$location
.path("/login");
} else if (status == 403) {
$location
.path("/accessForbidden");
} else {
}
return $q.reject(rejection);
}
};
});