Angular interceptor reload the view after get refresh token - angularjs

I have an implementation of JWT and when the token expires im get 401 and then i do this:
send request to get new token.
2.resend what the user request before.
Now before this the data isnt display in the page because the token is invalid, but now after the token is valid and i resend the request the user made, how i can refresh the state or the controller to auto show the data that the user ask before?
'responseError': function(rejection) {
if (rejection.status === 401) {
var $http = $injector.get('$http');
AuthService.getRefreshToken().then(function(res) {
//here im sending back the original user request again
//and now when the data is coming back because the token is valid now i want display that to the user
return $http(rejection.config);
});
}
}

I had the same problem and i use this module:
angular-http-auth
After you add this to your app, just add this code:
'responseError': function(rejection) {
if (rejection.status === 401) {
AuthService.getRefreshToken().then(function(res) {
// make sure you are inject the authService
authService.loginConfirmed();
});
}
}
And thats all, your code will continue to work.

Related

Simple interceptor that will fetch all requests and add the jwt token to its authorization header

In my efforts to setup login required to protected pages and reroute to the login page if not authorized, while using Django REST Framework and DRF-JWT, I am trying to go through the following tutorial:
https://www.octobot.io/blog/2016-11-11-json-web-token-jwt-authentication-in-a-djangoangularjs-web-app/
I am not sure what this looks like in step 3 of the front-end section.
// Add a simple interceptor that will fetch all requests and add the jwt token to its authorization header.
Can someone provide an example?
Also, my original post regarding the issues I am having setting this up in general.
Trying to get login required to work when trying to access protected pages
Thanks!
The interceptors are service factories that are registered with the
$httpProvider by adding them to the $httpProvider.interceptors array.
The factory is called and injected with dependencies (if specified)
and returns the interceptor.
The basic idea behind intercepter is that it will be called before each $http request and you could use a service to check if user is logged in and add a token or anything else that needs to be added into the header.You could also add some logic for response for each $http request, like handling the response based on status code.
Here is how you can use it in angular for adding the access token for each http request.
angular.module('myapp')
.run(['$rootScope', '$injector', function($rootScope,$injector) {
$injector.get("$http").defaults.transformRequest = function(data, headersGetter) {
if (sessionService.isLogged()) {
headersGetter()['Authorization'] = "Bearer " + sessionService.getAccessToken();
}
if (data) {
return angular.toJson(data);
}
};
});
Here is how you can use response intercepter:
angular.module('myapp')
.factory('authHttpResponseInterceptor', function($q, $location, sessionService, $http) {
return {
response: function(response) {
//some logic here
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status === 401) {
//some logic here
}
return $q.reject(rejection);
}
}
});

angularjs http interceptor to show error on loaded location path

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.

AngularJS login interceptor crashing chrome

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')

Determine if bearer token has expired or is just authorized

My angular application is making use of bearer tokens as outlined in the article series http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/. I have followed the forked example to seamlessly refresh tokens when the access token has expired (via 401 http code).
My question is how can I determine if the bearer token is expired or just plain unauthorized based on the role determined?
For example, my web api method has the attribute [Authorize(Roles="Admin")]. When I make a call to that, I get back my 401 error, which is expected. However, when my access token expires and I make another web api method call, it also returns a 401 error. Heres my responseError handler in my interceptor:
responseError: function (rejection) {
var deferred = q.defer();
if (rejection.status === 401) {
var authService = $injector.get('authService');
authService.refreshToken().then(function (response) {
_retryHttpRequest(rejection.config, deferred);
}, function () {
authService.logOut();
$location.path('/dashboard');
deferred.reject(rejection);
});
} else {
deferred.reject(rejection);
}
return deferred.promise;
}
I was playing around with different things but basically, I'd like to refresh my token and resend my request when the access token has expired; however, I don't want to refresh my token if it truly is a denied request due to the role specified.
Any thoughts?
As noted in my response to Cory Silva's comment, the Web API Authorize attribute will always return 401 unauthorized for both authentication AND authorization.
See article and thread below:
http://leastprivilege.com/2014/10/02/401-vs-403/
Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?
It looks like there are two options:
When I store the token retrieved from my authorization server in localStorage, I also store the token's expiration. In the interceptor responseError function, I compare the stored token expiration with the current datetime. If it's determined to be expired, refresh the token and resend the request.
responseError: function (rejection) {
var deferred = q.defer();
if (rejection.status === 401) {
var tokenExpired = false;
var authData = localStorage.get('authorizationData');
if (authData) {
tokenExpired = moment().isAfter(authData.expiration);
}
if (tokenExpired) {
var authService = auth;//$injector.get('authService');
authService.refreshToken().then(function (response) {
_retryHttpRequest(rejection.config, deferred);
}, function () {
authService.logOut();
$state.go('error');
deferred.reject(rejection);
});
}
else {
$state.go('error');
deferred.reject(rejection);
}
} else {
$state.go('error');
deferred.reject(rejection);
}
return deferred.promise;
}
Use the accepted answer in the stackoverflow thread I referenced above and create my own AuthorizeAttribute to determine token expiration vs. unauthorized access.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(actionContext);
}
}
}
I think I'm going to use option 2 so that the error codes a little clearer to the client.

Handle Angular 401 responses

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

Resources