witoldsz / angular-http-auth Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
response.status
is always 0
instead of 401
when the Server returns Authorization Required and you're doing CORS in IE10 on Windows 7.
if (response.status === 401 || response.status === 0) {
//Handle error here
}
Everytime you hit refresh in your browser and some AJAX calls are not finished, the error callback gets triggered with a response.status
of 0
.
var isPageBeingRefreshed = false;
window.onbeforeunload = function () {
'use strict';
isPageBeingRefreshed = true;
};
var myApp = angular.module('myApp', ['http-auth-interceptor']);
and check it against response.status
in the error callback
if (response.status === 401 || (response.status === 0 && !isPageBeingRefreshed)) {
//Handle error here
}
My first try was to check xhr.readyState
. This failed because it's always 4, also if you're hitting reload and the call was not finished at all.
The error were reported around Dec 12. I was not able to find any other updates. Maybe theres a better solution or hopefully a bugfix soon.
I'm also curious if someone can verify this bug in IE10 on Windows 8.
Hello! First of all good job on the interceptor :)
I have a scenario wherein i have multiple 401 response from my server
and each 401 response is intercepted by angular-http-auth then the event:auth-loginRequired is
triggered.
This will result into multiple request for a new token invalidating the previous ones.
Do you have a sample or can give some advice on how to handle this kind of scenario?
Cheers!
I'm using angular-http-auth with an API that uses a token in a header for authentication. I am capturing 401 responses and prompting the user to create a token. Once this token is created I would then like to add it to the headers of any further requests and also the request that has been deferred.
Passing a function to setDefaultHeaders would allow me to use $cookieStore to fetch the token for further requests. Adding the header to the deferred request I have no idea how to do.
It seems that the interceptor is not working on iOS with angular 1.2
Nothing gets intercepted on http calls. Anyone else with this issue?
Tested on a iphone 5, using phonegap / angularjs.
It works with angularjs 1.2.0rc2 but not the stable 1.2
If 'event:auth-loginRequired' triggers a redirect to a login page and
if the 'do-something-with-response' handler modifies the $scope,
upon 'authService.loginConfirmed()',
the reference to $scope is out of date (a new scope was generated upon navigating back to the page that generated the 401).
This causes all sorts of problems such as double requests in controllers etc...
Any idea how to implement this with a hard-redirect?
Could you consider adding support for commonJS so we can install angular-http-auth with NPM ?
The work is already done in forks like this one : https://github.com/twalling/angular-http-auth
If I submit a WWW-Authenticate
header back with the 401 response using Basic authentication, I still see the browser window pop-up. Any idea as to how I could block the browser's pop-up? I need to be able to authenticate users in the browser for other scenarios.
I bumped into a problem with angular-http-auth (a great lib I enjoyed using and reading the code..) which I managed to solve and was wandering if you would be interested in a PR along those lines:
The problem: We are doing auth with a securitycode/token in params or Authorization header (depending on post or get) , so when I get a 401 from the server, the saved http request includes the wrong param. while the relogin scenario does refresh the security code to an updated one, the buffered http request is retried and creates a 401 once again.
to work around this problem I patched http-auth to receive an object on confirmLogin, that includes the param name to be changed and the new security token, then in retryAll
I patch buffer[i].config.params.someparam
and buffer[i].config.headers.anotherparam
with the new data (if they exists..)
what do you think?
For example, if we replace this function https://github.com/witoldsz/angular-http-auth/blob/gh-pages/app/content.js#L13 with following code:
$scope.restrictedAction = function() {
$http.post('data/protected', $scope.restrictedData).success(function(response) {
// this piece of code will not be executed until user is authenticated
$scope.restrictedContent.push(response);
}).error(function() { console.log('forever alone.') });
}
the error callback will never execute no matter what happen.
In the authentication process, if user enter wrong authentication information, we should get response to know what happens if something is wrong, but here the error callback never been execute.
From what I gather:
So a simple fix would change failed login from 401 to something else, but that does seem like the best options for incorrect credentials. Any ideas how to mitigate this behaviour gracefully or just change error code for login requests?
I think It would be helpful to have a loginFailed routine that did not clear the buffer, and broadcast a message indicating a failed login attempt.
Mostly because the other related messages are controlled from the authService, so it makes sense to group them together. It would probably be invoked by an external module.
For example: I open a modal login box on event:auth-loginConfirmed
. If I receive this event again while the modal is still open, I know the login failed (just using basic auth and getting a 401 when attempting with the credentials). I could call loginFailed
to broadcast an event that could be used to display an error and maybe increment a failed attempt counter...
I'm short on time this week, but I can probably add this in next week and send a pull request.
I tried to reference this module with requirejs however it keep throwing exception on unknown provider http-auth-interceptor.
Here is my main.js
requirejs.config({
baseUrl: 'app',
urlArgs: 'bust=v1' + (new Date().getTime()),
waitSeconds: 20, // make sure it is enough to load all scripts
paths: {
angular: '../lib/ionic/js/angular/angular.min',
angularAnimate: '../lib/ionic/js/angular/angular-animate.min',
angularSanitize: '../lib/ionic/js/angular/angular-sanitize.min',
angularFilter: '../lib/ionic/js/angular/angular-filter.min',
angularMocks: '../lib/ionic/js/angular/angular-mocks',
uiRouter: '../lib/ionic/js/angular-ui/angular-ui-router.min',
ionic: '../lib/ionic/js/ionic.min',
ionicAngular: '../lib/ionic/js/ionic-angular.min',
text: '../lib/ionic/js/text',
httpAuthInterceptor: '../lib/http-auth-interceptor'
},
shim: {
angular : {exports : 'angular'},
angularAnimate : {deps: ['angular']},
angularSanitize: { deps: ['angular'] },
angularFilter: { deps: ['angular'], exports: 'angular.filter' },
angularMocks : {deps :['angular']},
uiRouter : {deps: ['angular']},
ionic : {deps: ['angular'], exports : 'ionic'},
httpAuthInterceptor: { deps: ['angular'] },
ionicAngular: { deps: ['angular', 'ionic', 'uiRouter', 'angularAnimate', 'angularSanitize', 'angularFilter', 'httpAuthInterceptor', 'angularMocks'] },
},
priority: [
'angular',
'ionic'
],
deps: [
'bootstrap'
]
});
and the authenticate service
define(['../module'],
function (module) {
'use strict';
var name = 'AuthenticateService';
var dependencies = ['$rootScope', '$http', 'http-auth-interceptor'];
var service = function ($rootScope, $http, authService) {
console.log(authService);
return {
fake: function(){
console.log('fake login');
},
login: function (user) {
$http.post('https://login', { user: user }, { ignoreAuthModule: true })
.success(function (data, status, headers, config) {
$http.defaults.headers.common.Authorization = data.authorizationToken; // Step 1
// Need to inform the http-auth-interceptor that
// the user has logged in successfully. To do this, we pass in a function that
// will configure the request headers with the authorization token so
// previously failed requests(aka with status == 401) will be resent with the
// authorization token placed in the header
authService.loginConfirmed(data, function (config) { // Step 2 & 3
config.headers.Authorization = data.authorizationToken;
return config;
});
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast('event:auth-login-failed', status);
});
},
logout: function (user) {
$http.post('https://logout', {}, { ignoreAuthModule: true })
.finally(function (data) {
delete $http.defaults.headers.common.Authorization;
$rootScope.$broadcast('event:auth-logout-complete');
});
},
loginCancelled: function () {
authService.loginCancelled();
}
}
};
module.factory(name, dependencies.concat(service));
});
Hi,
Thanks for this great module.
My issue is the following:
The first log in works perfectly. But then, if I log out and try to log in again, the http request that it is stored in the buffer is sent without the http authorization headers. If I continue browsing the site, the following http requests do carry the authorization token. So the problem it is just in the buffered request after a log out and a new log in.
This is my first angularjs app, so probably I'm doing something wrong, but I have been hours trying to fix this and I do not know what else I can try. I have followed the demo, and in my LoginController, I have something like this for the submit button of the login form:
$http.post('api/login', loginInfo).success(function(data) {
$http.defaults.headers.common['Authorization'] = 'Basic ' + data.token;
authService.loginConfirmed();
});
Am I using the module correctly? Any hint regarding why the first log in works like a charm but if I log out and then log in, the buffered request is sent without the token?
Thank you very much.
When my login fails, the 401 errors get added to the httpBuffer. Thus, the login doesn't go away after success.
Looking through the source and the comments, I realize that this module looks for response.config.ignoreAuthModule when checking to add it to the httpBuffer, but wasn't not sure where to add it.
After some trial and error, here's an example of how this can be done:
login: function(username, password) {
var promise = $http({
method: 'POST',
url: CONFIG.BASEURL+'login',
data: 'uname=' + username + '&pwd=' + password,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
ignoreAuthModule: true
})
.then(function(response) {
...
authService.loginConfirmed();
});
return promise;
}
Hope that helps somebody else.
I am not sure if this issue at all but something to discuss atleast.
What is the case of $http.defaults.headers.common?
I put my auth-token in there on successful login, then auth-interceptor replies failed xhrs but that auth-tokens is not there.
Of course because of this authentication failes again in backend so login will pop up again..
So should these $http.defaults.headers.common -headers always add on the top of the config?
function retryHttpRequest(config, deferred) {
function successCallback(response) {
deferred.resolve(response);
}
function errorCallback(response) {
deferred.reject(response);
}
$http = $http || $injector.get('$http');
$http(config).then(successCallback, errorCallback);
}
or how this case should be handled?
I have forked the demo to demonstrate this potential bug, which can be found here: https://github.com/arnoudsietsema/angular-http-auth/tree/gh-pages
The 'server' response has a 50% chance of returning an error (http code 500). In the unauthenticated section the error shows up in the $http().error() method as expected.
For the authenticated request however something odd happens. When the error is throws after the login request was successfully executed, the $http().error () method is not called when the 500 error is returned.
Is this a bug or did I not implement this correctly?
Sorry this is not really an issue, but i don't know where to post this. I would like to use angular-http-auth with $resource. is it possible ?
i asked it here : http://stackoverflow.com/questions/18776248
thanks
In 1.1.4, $httpProvider.responseInterceptors has been deprecated. The new format ($httpProvider.interceptors) can be seen here: http://code.angularjs.org/1.1.4/docs/api/ng.$http
It would also probably be a good idea to make it resort to the old way if $httpProvider.interceptors doesn't exist, for backwards compatibility.
It certainly has been useful for me when working with tokens. Are people wary of supporting a non-standard status code like 419?
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
switch (rejection.status) {
case 419:
case 401:
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
case 403:
$rootScope.$broadcast('event:auth-forbidden', rejection);
break;
}
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
}]);
Otherwise, I'd love to take care of it :)
It's not possible to evaluate the response in case of a failed login if the server responsed with a 401.
Set the ignoreAuthModule
flag on the server response in case of a failed login attempt.
I have two ideas how to improve the interceptor
What do you think?
Hello =)
I'm using this plugin in one of my projects and now I'm facing one problem that I weren't aware before.
If I place one custom interceptor of mine, even if looking over other status code (like one 409
, for example), this plugin stops and don't does nothing that it was capable of before. It seems like that it overwrites your plugin and, because some lack of knowledge, I'm not sure if angular
can support more than one interceptor of one kind (one responseSuccess
or else).
Is this correct or is there something that we are missing?
Thanks in advance, awesome plugin!
I'm using this interceptor to integrate with google authentication; this is done simply by listening to the "login required" event that will be broadcast when google answers a request with a 401 response. The event handler uses google APIs to get a token and invokes the loginConfirmed method in the authService.
The problem I'm facing is that this causes an infinite loop, since the requests will be retried but their configuration won't include the token and they will fail again with status 401. I'm thinking about adding a parameter to the loginConfirmed method to pass in a "http updater function" that will be invoked on each configuration object of the httpBuffer before retrying the request (so that in my case I can set the Authorization header).
Does this sound like a viable solution? Are you interested in such a patch or does anyone have a better idea to get around this issue?
Thank you!
Any chance of a more complete doc or fuller example (with controller)?
Thanks
Dear Friends.
I did my research but i did not find any solution to add the token if it is already exist.
I know that the updater is supposed to add the token to header after s successful log-in but what about a case when user is already logged in and has a valid token? there should be a function to add the token to any request if the token is available.
is there anything that I am missing here?
I suggest a request function to the interceptors to add the header. is there any similar functionality that i am not aware of?
Currently I am loading fixed route after successful log-in.
Is there a way to load requested route after user successful logged in?
$scope.$on('event:auth-loginConfirmed', function() {
growl.addSuccessMessage("You are Logged in!");
$state.go('dashboard');
});
$scope.$on('event:auth-loginCancelled', function() {
growl.addWarnMessage("login Cancelled!");
$state.go('home');
});
When same unauthenticated url is requested multiple times, interceptors stores this url multiple times.
This could be solved by changing the buffer to associative array as save request with url as key and then using for-in loop in retryAll() function. So if same url request is already in buffer it gets replaced with new request.
I'm not very experienced in JavaScript, nor AngularJS. So maybe this is not a good idea for some reason. But I can't see any problem so far. What do you think?
https://github.com/witoldsz/angular-http-auth/blob/master/src/http-auth-interceptor.js#L23
After login and before replay of all buffered requests, I need to modify the headers so that they include a required Authorization header with username and apikey.
I'm new to angular, but would it be good enough to do something like :
https://github.com/edmenendez/django-angular-auth/blob/master/static/js/controllers.js#L27
I feel that the auth interceptor no longer works with the latest version of angular (1.3)
There has been beaking exchange on $http : API interceptors changed
On my app I can no longer connect . But there is no error in the console.
Hi,
Can someone tell me how to use angular-http-auth? Is there any documentation for using it? It would be really helpful
I'm not sure if this is the case with other people but I seem to have an issue in IE where the cookies are not sent when replaying the cached requests after a HTTP 401.
The code works perfectly in Chrome and Firefox.
Anyone seen this situation or know anything about it?
Thanks
J
This is a nice idea.
I'm wondering how to support Canceling from the login "screen."
I was attempting to add 403/Forbidden support and working on a pull request for it.... and wanted to add a cancel button to the login screen... but it looks like the initial request that prompts the login to occur will keep trying to resolve until you actually log in -- is that true? Or am i missing something? Does the httpBuffer need to be cleared? Or a promise rejected?
When event:auth-loginRequired event is fired on a route change promise, the location has already changed to the new route. How can i revert that location change on the auth-loginCancelled event?
We get this question a lot and the implementation you have here seems pretty simple/straightforwards. We're always looking for more team members and you'd get a lot more support for further development.
I am trying to integrate http-auth-interceptor into mean.io (express / angular), but keep receiving the following error:
Error: [$injector:modulerr] Failed to instantiate module mean due to:
Error: [$injector:modulerr] Failed to instantiate module http-auth-interceptor due to:
Error: [$injector:nomod] Module 'http-auth-interceptor' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
I am using AngularJS 1.2.4 and I have http-auth-interceptor.js located in /public/lib/http-auth-interceptor/http-auth-interceptor.js
This is what my app.js looks like
angular.module('mean', [
'http-auth-interceptor',
'ngCookies',
'ngResource',
'ngRoute',
'ui.bootstrap',
'ui.route',
'mean.system',
'mean.articles',
'login'])
/**
* This directive will find itself inside HTML as a class,
* and will remove that class, so CSS will remove loading image and show app content.
* It is also responsible for showing/hiding login form.
*/
.directive('meanApplication', function() {
return {
restrict: 'C',
link: function(scope, elem, attrs) {
//once Angular is started, remove class:
elem.removeClass('waiting-for-angular');
var login = elem.find('#login-holder');
var main = elem.find('#content');
login.hide();
scope.$on('event:auth-loginRequired', function() {
login.slideDown('slow', function() {
main.hide();
});
});
scope.$on('event:auth-loginConfirmed', function() {
main.show();
login.slideUp();
});
}
};
});
angular.module('mean.system', []);
angular.module('mean.articles', []);
angular.module('login', []);
And my login.js controller looks like this:
angular.module('login', ['http-auth-interceptor']).controller('LoginController', ['$scope', '$http', '$cookies', '$routeParams', '$location', 'Global' function ($scope, $http, authService, $cookies, $routeParams, $location, Global) {
$scope.global = Global;
$scope.csrfToken = $cookies['XSRF-TOKEN'];
$scope.submit = function() {
$http.post('/users/session').success(function() {
authService.loginConfirmed();
});
};
}]);
And I am loading http-auth-interceptor.js in the footer like this:
<!--AngularJS-->
<script type="text/javascript" src="/lib/angular/angular.js"></script>
<script type="text/javascript" src="/lib/angular-cookies/angular-cookies.js"></script>
<script type="text/javascript" src="/lib/angular-resource/angular-resource.js"></script>
<script type="text/javascript" src="/lib/angular-route/angular-route.js"></script>
<script type="text/javascript" src="/lib/http-auth-interceptor/http-auth-interceptor.js"></script>
<!--Angular UI-->
<script type="text/javascript" src="/lib/angular-bootstrap/ui-bootstrap.js"></script>
<script type="text/javascript" src="/lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script type="text/javascript" src="/lib/angular-ui-utils/modules/route/route.js"></script>
<!--Application Init-->
<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript" src="/js/config.js"></script>
<script type="text/javascript" src="/js/directives.js"></script>
<script type="text/javascript" src="/js/filters.js"></script>
<!--Application Services-->
<script type="text/javascript" src="/js/services/global.js"></script>
<script type="text/javascript" src="/js/services/articles.js"></script>
<!--Application Controllers-->
<script type="text/javascript" src="/js/controllers/login.js"></script>
<script type="text/javascript" src="/js/controllers/articles.js"></script>
<script type="text/javascript" src="/js/controllers/index.js"></script>
<script type="text/javascript" src="/js/controllers/header.js"> </script>
<script type="text/javascript" src="/js/init.js"></script>
When the page is loaded I am able to click on and open http-auth-interceptor.js from view-source. I know I am missing something obvious, please let me know if you need any more information.
Thanks, DH
For some reason, adding url to ignored urls still triggers the login required event to fire.
Am I doing something wrong?
Just curious, why don't you just perform a $route.reload() to redo all the previously unauthorized requests?
PS: Thanks for the great tutorial!
I saw these lines in the buffer code:
/*
* Service initialized later because of circular dependency problem.
*/
var $http;
And then:
$http = $http || $injector.get('$http');
What exactly is this circular dependency? Isn't $http
a stock Angular service?
I'm just trying to understand exactly how your code works. Thank you in advance!
It would be nice to be able to clear the buffer as we may have duplicate requests, eg.
Hi,
Thanks for the code, this was the most well done version of this interceptor I found. Just wanted to suggest one small change: How do you feel changing authService to something more unique? It's a too generic name for a third party service and prevent's me to creating my own authService. Have you considered naming it httpAuthService or something?
At present, this doesn't seem to work with CORS, as I see the requests failing. Would it be possible to add support for this? Do you have a sample showing this working?
It's a breaking change but one I would personally love :)
Instead of referencing authService, you could provide the loginConfirmed and loginCancelled callbacks directly on the event arguments of the broadcast. That would remove a dependency. Example:
$rootScope.$on('event:auth-loginRequired', function (args) {
signin().then(
function() {
args.loginConfirmed();
},
function() {
args.loginCancelled();
});
});
Let me know if you'd like me to suggest a pull request!
I am using angular UI router that supports nested views. My nested views invoke multiple REST services at same time for for a given URL .when two REST calls receive 401 error, this module is broadcasting two loginRequired events.
This causes my login dialog pops up again after successful login. It would be nice if this module Wait for loginConfirmed() call before it broadcast next loginRequired event.
The authService has only one method: #loginConfirmed().
loginCancelled: function(data, reason) {
httpBuffer.rejectAll(reason);
$rootScope.$broadcast('event:auth-loginCancelled', data);
}
Updating the readme to include info on how to use the loginCancelled method would make it easier for people to get started with angular-http-auth.
Maybe you tell me examples of implementation? Thanks.
Thanks Witold for the efforts and for putting the library into bower. I'm wondering why the file is called 'angular-http-auth' and the module 'http-auth-interceptor'? A more consistent naming would be convenient. Also the library should be directly in the folder, not in the subfolder /src, to stick to bower defaults.
Hi,
Any ideas how this would all work with a redirection-based SSO protocol like CAS? In these systems, the login takes place on a page served by the IDP website (i.e., not your app), then your browser gets redirected back to your app with a token in the query string -- you never see the username / password, and couldn't do anything useful with it even if you did.
Hi,
I found this module very useful for my usecase. However, I had issue with Authorization header not being updated. It had been sending the same header again and again. The code (after token refresh) was the following:
$http.defaults.headers.common['Authorization'] = 'Token token="' + newToken + '"';
authService.loginConfirmed();
but that didn't update the Authorization header for retried requests. After digging around for a couple of hours, I found out that the loginConfirmed
method had second argument named configUpdater
. I solved the problem by using it:
$http.defaults.headers.common['Authorization'] = 'Token token="' + newToken + '"';
authService.loginConfirmed(null, function(config) {
config.headers['Authorization'] = 'Token token="' + newToken + '"';
return config;
});
I hope this could save someone else's time, and it would be great if you could add some documentation for configUpdater
.
Thanks,
Danijel
Hi there! You have not assigned any license to this project which makes its usage (and modification) difficult from a legal perspective. For a quick blurb about what this actually means check out http://choosealicense.com/no-license/
...but generally speaking, the absence of a license means that default copyright laws apply. This means that you retain all rights to your source code and that nobody else may reproduce, distribute, or create derivative works from your work. This might not be what you intend.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.