I have an application for which I created an interceptor to handle token expirations after 15 minute inactivity, it successfully redirects to the login page after a token has expired, but Im not able to show the error after redirecting to the login page.
My question is, how can I show the user the token expired error on the login page, after the interceptor has redirected the app to that page.
Heres my redirector:
app
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(function($q, $location, LoopBackAuth) {
return {
responseError: function(rejection) {
if (rejection.status == 401) {
//Now clearing the loopback values from client browser for safe logout...
LoopBackAuth.clearUser();
LoopBackAuth.clearStorage();
$location.path("/login");
}
return $q.reject(rejection);
}
};
})
}])
.config(function(LoopBackResourceProvider) {
LoopBackResourceProvider.setAuthHeader('X-Access-Token');
})
Finally and thanks to #forrestmid to point me in the right direction this is what I ended up doing.
on the http interceptor just added:
$location.path("/login").search({error: 'invalid_token'});
and then on the controller just had to do:
var queryString = $location.search();
$scope.errors = {};
if (queryString && queryString.error) {
$scope.errors = {
'invalid_token': {
code: 'invalid_token'
}
}
}
now on the template I already have logic to handle the error object so now it works fine :)
Referencing this post in regards to injecting the $state service into an HTTP interceptor:
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(function($q, $injector, LoopBackAuth) {
return {
responseError: function(rejection) {
if (rejection.status == 401) {
//Now clearing the loopback values from client browser for safe logout...
LoopBackAuth.clearUser();
LoopBackAuth.clearStorage();
$injector.get('$state').go('app.login', {error: 'Token expired.'});
}
return $q.reject(rejection);
}
};
})
}]);
Assuming that you're using ui.router:
app.config(function($stateProvider){
$stateProvider
.state("app",{abstract: true})
.state("app.login", {
url: "/login",
params: {error: ""}
});
});
By default there will be no error when transitioning to the app.login state, but when there is a param error set to whatever, it can display the error. This will be in the $stateParams.error variable on your login page.
Let me know if I botched any of that code since I didn't test it. The line I think you want is the $injector line.
Related
Im working with angularjs and Im trying to handle errors with interceptors.
I have run into the issue of how handling the different error session expires with login failed when server replies both with 401.
It seems that interceptors defined in the config will execute before any other interceptors (order definition matters):
var configInterceptor = function($httpProvider)
{
$httpProvider.interceptors.push('unauthorizedInterceptor');
};
angular.module('app', [])
.config(configInterceptor)
.controller....
The interceptors defined in the $resource will be considered only after they have gone through the configInterceptor.
var res = $resource(serviceUrl + '/users/login',{},{
post:{
method:'POST',
params: {},
withCredentials: true,
interceptor: {
responseError: function(){
console.log('login interceptor');
}
}
}
});
I would like to have a single point for controlling when the session has expired (pushing the user to the login page and sending an appropriate message) without the need to add the unauthorizedInterceptor to all $resources, one by one.
If the error is due to users trying to log in and failed, then the interceptor should treat it differently (message will be different).
Any way to resolve this properly? I tried also defining interceptors to only be applied to a specific module but they are triggered.
I would do something like that:
angular.module('app', []).factory('unauthorizedInterceptor', unauthorizedInterceptor);
function unauthorizedInterceptor($q, ngUserAuthService) {
return {
'responseError': function (response) {
if (response.status === 401) {
// find out if the session has expired or the user login has failed
if (sessionExpired()) {
doRedirectToSomewhere();
} else if (loginHasFailed()) {
response.loginFailed = true; // use this later
}
}
return $q.reject(response);
}
};
}
And then only add an interceptor to the $resource when you want to check for failed login:
var res = $resource(serviceUrl + '/users/login',{},{
post:{
method:'POST',
params: {},
withCredentials: true,
interceptor: {
responseError: function(response) {
console.log('login interceptor');
console.log('The login has failed: ' + response.loginFailed);
}
}
}
});
My website uses AngularJS and UI Router everywhere except for the login page. To redirect a user to the login page when their session expires, I have the following interceptor
angular.module('myApp').factory('authInterceptor', ['$q', function ($q) {
return {
'request': function (config) {
return config || $q.when(config);
},
'response': function (response) {
if (response.status === 401) {
window.location.href = "App/Signin";
return $q.reject(response);
} else {
return response || $q.when(response);
}
},
'responseError': function (rejection) {
if (rejection.status === 401) {
window.location.href = "App/Signin";
}
return $q.reject(rejection);
}
};
}]);
Sometimes, this code causes Chrome to fall into a redirect loop and crash. Clicking on the reload button resolves the problem. I suspect that UI router is trapping the redirect to the login page and trying to set the state back to the default. Is there a way I can redirect to the login page without having UI router interfere?
The loop caused by the fact that redirect to the login page launch another redirection recursivley
Please add the following to match your scenario
if (response.status === 401 && $location.path() != 'App/Signin')
And to make it clean, use the $state service, for example:
$state.go('login-signin-state')
I am working on a project using laravel and angularjs. I am using only Laravel to authenticate the users and when their logged in, then angularjs ui veiw will handle the navigation. when doing this I realized a problem, when the session has expire the user should be redirected to the logged in page based on the auth filter that is set on the route. Additionally when I checked the browser dev tool network tab, I see that the sign in page is send as a response. I am wondering how can I make my project redirect the user to the logged in page when the session has expire. how can I solve this problem and Thanks in advance for the assistance.
You can do that with $httpInterceptor, here is demo code:
var myApp = angular.module("MyApp", []);
myApp.config(function ($httpProvider, $provide) {
$provide.factory('myHttpInterceptor', function ($q, $location) {
return {
'response': function (response) {
//you can handle you sucess response here.
return response;
},
'responseError': function (rejection) {
console.log(rejection);
//if(rejection.data.xxx==="xxx")
if(rejection.status === 408){//session expired code
alert('logout!');
// clear your local data here...
$location.url("/login")
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
});
myApp.controller("MainController", function ($scope, $http) {
$scope.response = {};
$scope.triggerGet = function () {
$http.get("/my/json").success(function (data) {
$scope.response = data;
});
};
});
When your server side response is session expired, you can handle the response.status or you can handle the other data with response.data.
Here is $httpInterceptor document.(In the middle of the page)
To redirect the user client-side in JavaScript use location. https://developer.mozilla.org/en-US/docs/Web/API/Location
For this case I think you want to look at location.assign() specifically.
I have simple api and a authorization point
when i request to api i get a 401 if the token is invalid (token loses validity past five minutes).
i know i can intercept 401 for example with
app.factory("HttpErrorInterceptorModule", ["$q", "$rootScope", "$location",
function($q, $rootScope, $location) {
var success = function(response) {
// pass through
return response;
},
error = function(response) {
if(response.status === 401) {
// dostuff
}
return $q.reject(response);
};
return function(httpPromise) {
return httpPromise.then(success, error);
};
}
]).config(["$httpProvider",
function($httpProvider) {
$httpProvider.responseInterceptors.push("HttpErrorInterceptorModule");
}
]);
but i want capture and queue the request and show a login form if is success then change the token (it's a header) and execute request again
You can use $httpInterceptor in slightly another way. If you want to redirect user after login to page where user actually failed you need to cache failed request in some service and then redirect user somewhere after login (I beleive in logic connected to your login).
But you may need to have some test endpoint to protect your controllers from unrestricted access, you might want to use resolve https://thinkster.io/egghead/resolve/
So in this case you will receive error connected with restricted access to proctedted endpoint but not to your page.
To solve this problem I used marker param (or header) to find out where I should redirect user after login.
Here is example of your httpInterceptor.
angular.factory('httpInterceptor', function ($q, $rootScope, $log, someService) {
return {
request: function (config) {
return config || $q.when(config)
},
response: function (response) {
return response || $q.when(response);
},
responseError: function (response) {
if (response.status === 401) {
//here I preserve login page
someService
.setRestrictedPageBeforeLogin(
extractPreservedInfoAboutPage(response)
)
$rootScope.$broadcast('error')
}
return $q.reject(response);
}
};
})
.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
});
angular-http-auth module provides a service that intercepts requests and queques them to re-send them later once a user logs in.
This service fires also these events below, so you could listen to them and decide what to show on screen
event:auth-loginRequired
event:auth-loginCancelled
event:aut-loginConfirmed
Look at the code. It has just a few lines of code
https://github.com/witoldsz/angular-http-auth
I have an angularjs project. I am just wondering, how to show the user a message, if a requested view/partial is not found (HTTP 404). At the moment, angular starts the request and gets a 404 response including the error-html, but the user doesn't see any change to the website.
Add an $http interceptor (scroll down on this page) for 'responseError'
angular.module("app").config(function($provide, $httpProvider) {
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
'responseError': function(response) {
if (response.status == 404) {
// user hit a 404 -- you can check response.url to see if it matches
// your template directory and act accordingly
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
});