Angular resend the request after change the token - javascript

I have auth interceptor and JWT authentication.
When token expires im getting 401 and i need to send request to refresh token.
After the token is refreshed i want to resend the user request but it is not working.
For example, to go to my account tab, user need to have valid token,he click on my account and get 401 and then the refresh token is sent and the valid token is saved in the local storage.
Now what i want that after this the interceptor auto make the action of the user, in this case to go to my account but my way doesnt working, how can i do this?
The interceptor:
'responseError': function(rejection) {
var $state = $injector.get('$state');
var deffer = $q.defer();
if (rejection.status === 401) {
var $http = $injector.get('$http');
var AuthService = $injector.get('AuthService');
var user = store.get('user');
var cred = AuthService.refreshTokenValue(user.secretId, user.secretClient, user.refreshToken);
AuthService.getRefreshToken(cred).then(function(res) {
UserService.oAuth.accessToken = res.data.access_token;
// here i want to resend the request
return deffer.resolve(rejection);
});
}
return $q.reject(rejection);
}

you should first create a promise object to return then retry request using $http(rejection.config),
resolve or reject promise according to your authorization process.
Sample Code
var $state = $injector.get('$state');
var retryRequest = function($http, config, deferred) {
function successCallback(response) {
deferred.resolve(response);
}
function errorCallback(response) {
deferred.reject(response);
}
$http(config).then(successCallback, errorCallback);
}
if (rejection.status === 401) {
var deferred = $q.defer(); //moved deferred to here
var $http = $injector.get('$http');
var AuthService = $injector.get('AuthService');
var user = store.get('user');
var cred = AuthService.refreshTokenValue(user.secretId, user.secretClient, user.refreshToken);
AuthService.getRefreshToken(cred).then(function(res) {
UserService.oAuth.accessToken = res.data.access_token;
retryRequest($http, rejection.config, deferred);
// here i want to resend the request
return deffer.resolve(rejection);
});
return deferred.promise;
}
return $q.reject(rejection);
but you should better use an httpBuffer to queue requests and retry them all once authorization succeeds
Edit: you can use something like this https://github.com/witoldsz/angular-http-auth/blob/master/src/http-auth-interceptor.js

Related

Loading a new HTML page with $http request

I'm dealing with tokens, and with every HTTP request, the token will be added to the header so the server can tell if user is logged in or not.
Because of that, I can't redirect to the specific URL and check for tokens because the token wont be added to the header.
Is it possible to load in a new HTML page from an http request? Every time the server responds, I get the code of the HTML page. I think angular doesn't reload the new incoming page.
Edit: Here is some code
Code that adds to every http request
// ===================================================
// application configuration to integrate token into requests
// ===================================================
.factory('AuthInterceptor', function($q, $location, AuthToken) {
var interceptorFactory = {};
// this will happen on all HTTP requests
interceptorFactory.request = function(config) {
// grab the token
var token = AuthToken.getToken();
// if the token exists, add it to the header as x-access-token
if (token) {
config.headers['x-access-token'] = token;
}
return config;
};
// happens on response errors
interceptorFactory.responseError = function(response) {
// if our server returns a 403 forbidden response
if (response.status == 403) {
AuthToken.setToken();
$location.path('/login');
}
// return the errors from the server as a promise
return $q.reject(response);
};
return interceptorFactory;
});
I'm using ui-routing. I have a front-end and back-end of the site. So when a user logs in from the front end, the front-end.html goes away, and back-end.html gets loaded. But angular just reads the back-end.html code.
// function to handle login form
vm.doLogin = function() {
vm.processing = true;
//clear the error
vm.error = '';
Auth.login(vm.loginData.email, vm.loginData.password)
.success(function (data) {
vm.processing = false;
// if a user successfully logs in, redirect to main application
if (data.success)
return $http.get('/account');
//Here is where a user logs in and i redirect them to the backend of the site. But the response is the HTML code of the page. I want that page to load.
else
vm.error = data.message;
});
};
You are heading for the wrong direction.
You should read this:
http://blog.thesparktree.com/post/75952317665/angularjs-interceptors-globally-handle-401-and

UI freezes with multiple http calls in AngularJS

Please find below the angularjs factory method to call http request:
var HttpService = angular.module("HttpService",[]);
HttpService.factory("HttpServiceFactory",['$http', '$q', '$location', '$rootScope' ,function($http, $q, $location, $rootScope){
return {
getData: function(url, headers, bOnErrorRedirect, bShowInPageError, params){
var headerParam = {'Accept':'application/json'};
if(headers !== undefined || headers !== null){
headerParam = $.extend(headerParam, headers);
}
var updatedParams = {'TimeStamp':new Date().getTime()};
updatedParams = $.extend(params, updatedParams);
var deferred = $q.defer();
$http.get(url,{
headers: headerParam,
params : updatedParams
}).success(function(successResponse){
if(successResponse){
var responseJSON = angular.fromJson(successResponse);
if(responseJSON && responseJSON.messages && responseJSON.messages.length){
//Process Error
}else{
deferred.resolve(successResponse);
}
}else{
deferred.resolve(successResponse);
}
}).error(function(errorResponse , status){
//Process Error
console.error("status here:: "+status);
});
return deferred.promise;
}
}
}]);
And I am calling this method in controller with all required dependencies as below:
HttpServiceFactory.getData(args.sURL,null,false,true,args.oQueryParams).then(function(response){
scope.bDataLoading = false;
// process data
})
.catch(function(oResponse) {
scope.bDataLoading = false;
scope.bDisplayError = true;
// process error
});
Here everything works fine. But the issue is when I've multiple http calls on a page, the UI freezes and does not allow to interact till the request has been processed.
For example, on a page I am displaying 2 angular-ui-grid based on user's selected criteria by input box and calendar control. In such case, the UI freezes until both grids have been displayed or error message has been displayed.
During http service call, user can not do anything but simply wait to finish the request.
How do I resolve the issue of UI freezing ? Is it a true async behavior ? If not, what am I missing to achieve correct async behavior ?

AngularJS Unable to send JSON data to DB after adding redirect

I am trying to send JSON data to a backend database. The following code works fine until I add a redirect into the newPost function using "$window.location.href = 'success.html';" After adding the redirect, nothing is posted to the database. There are also no errors displayed in console. I assume, I should probably be checking if the post was successful but am unsure of how to properly do that.
app.controller('FormCtrl', function($scope, $filter, $window, getData, Post, randomString) {
// Get all posts
$scope.posts = Post.query();
// Form data for creating a new post with ng-model
$scope.postData = {};
$scope.$on('updateImage', function () {
$scope.postData.attachment = getData.image;
});
$scope.postData.userid = "Mango Farmer";
$scope.postData.uuid = randomString(32);
$scope.$on('updateGPS', function () {
$scope.postData.gps = getData.gps;
});
$scope.postData.devicedate = $filter('date')(new Date(),'yyyy-MM-dd HH:mm:ss');
$scope.newPost = function() {
var post = new Post($scope.postData);
console.log(post);
post.$save();
$window.location.href = 'success.html';
}
});
Successful Response from Server
RETURN CODE: 200
RETURN HEADERS:
Content-Type: application/json
RETURN BODY:
{
"ref":<string>,
"uuid":<string>
}
post.$save();
$window.location.href = 'success.html';
should be:
post.$save().then(function(response) {
$window.location.href = 'success.html';
});
I'm pretty sure thats right. Give it a try and let me know.

Object not being updated correctly inside Angular Service

I believe this has to do with the way JS Closures work, but I am not totally sure. I am using an AngularJS service to manage the life-cycle of a model that is used within my application. The service uses a combination of fetch() and save() to run GET and POST requests get and update the model from an API. After I fetch() the object, I attempt to place the result into an object sitting in the service where it can be fetched later on. My problem, is that after a successful save(), I take the result and place it into the same object to essentially "update" the object that is on the client with the correct object that is on the server (hence the result of the POST is just an echo of the payload if all is successful).
The problem is that my object is not persisting, and all subsequent calls to save() contain a "stale" object that is not completely updated.
Here is my service:
app.factory('MailboxSubscription', function (API, $q, $stateParams, $rootScope) {
var Subscription = null; //THIS IS MODEL THAT I TRY TO UPDATE CONSTANTLY
var isBusy = false;
var service = {};
var deferred;
var defaultFailure = function(res){
}
service.fetch = function (success, force, failure) {
if(!failure){ failure = defaultFailure;}
if(isBusy){
deferred.promise.then(success, failure);
return deferred.promise;
}else{
deferred = $q.defer();
}
if(Subscription && !force){ // ONCE THE MODEL HAS BEEN FETCHED ONCE, IT STAYS IN MEMORY AND ALL SUBSEQUENT CALLS WILL SKIP THE API CALL AND JUST RETURN THIS OBJECT
deferred.resolve(Subscription);
}else{
//Make the API call to get the data
//Make the API call to get the data
if(typeof(success) === 'function'){
var ServiceId = $stateParams.serviceId;
}else{
var ServiceId = success;
}
isBusy = true;
API.Backups.O365.Exchange.get({id : ServiceId || $stateParams.serviceId}, function(res){
isBusy = false;
if(res.success){
Subscription = res.result; // ON A FIRST-TIME FETCH, THIS API CALL IS USED TO GET THE MODEL
deferred.resolve(Subscription);
}else{
deferred.reject(res);
}
}, function(res){
isBusy = false;
deferred.reject(res);
});
}
deferred.promise.then(success, failure);
return deferred.promise;
}
service.save = function(success, failure){
if(!failure){ failure = function(){};}
if(!success){ success = function(){};}
var deferred = $q.defer();
API.Backups.O365.Exchange.update({id :$rootScope.ServiceId || $stateParams.serviceId}, Subscription, function(res){
if(res.success){
Subscription = res.result; // AFTER AN UPDATE IS MADE AND THE OBJECT IS SAVED, I TRY TO SET THE RESULT TO Subscription.
deferred.resolve(res);
}else{
deferred.reject(res);
}
}, function(res){
deferred.reject(res);
});
deferred.promise.then(success, failure);
return deferred.promise;
}
service.get = function(){
return Subscription;
}
return service;
});
So the problem appears to stem from trying to use Subscription as a centralized resource for storing the model, but the model is not updating correctly.
If you are looking to have that Subscription model updated throughout the service, I'd suggested when you call MailboxSubscription.fetch() and MailboxSubscription.save()in your controller, you use MailboxSubscription.get() in the .then() method of your calls.
// get initial value of Subscription model
$scope.Subscription = MailboxSubscription.get();
// let's fetch
MailboxSubscription.fetch().then(
// callback
function() {
// let's get the updated model
$scope.Subscription = MailboxSubscription.get();
},
// errback
function() {
// handle error
}
);
// let's save
MailboxSubscription.save().then(
// callback
function() {
// let's get the updated model
$scope.Subscription = MailboxSubscription.get();
},
// errback
function() {
// handle error
}
);
Also, I've created a working jsfiddle simplifying your use case. It works fine. Maybe there is something that can be gleamed from that (I am using $timeout to spoof your API calls).

$resource delete function not working as expected?

I have built a simple application in Angular consuming a simple API I created myself using Laravel. The application is hosted here. The API is hosted here. Now I can log in to the application at which point the API returns a simple auth_token which is sent as the URL parameter in every subsequent request that is sent to the server.
There is only one user in the system:
Email: admin#admin.com
Password: admin12345
You can log into the application using these credentials at which point the application will set a cookie using the $cookieStore service and will use the token in this cookie for every subsequent request. After using the application, a user can log out from the application, where a DELETE request is sent to the server and on the success method, the cookie is deleted from the browser.
Unfortunately there is some issue with the code I suppose. The DELETE request is working as expected and it deletes the auth_token on the server and returns 200 OK. But the success method is not called. I am not sure what I am doing wrong. It might be just a syntax problem.
app.js
function AppCtrl ($scope, $cookieStore, $location, Auth) {
$scope.setActive = function (type) {
$scope.destinationsActive = '';
$scope.flightsActive = '';
$scope.reservationsActive = '';
$scope[type + 'Active'] = 'active';
};
$scope.authenticate = function (credentials) {
Auth.save(credentials, function(data){
$cookieStore.put('auth_token', data.auth_token);
$scope.isLoggedIn = true;
$location.path('destinations');
$scope.message = null;
}, function(data){
$scope.message = "Email/Password combination incorrect!";
});
};
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete({
'auth_token': $cookieStore.get('auth_token')
}, function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
});
};
if($cookieStore.get('auth_token')){
$scope.isLoggedIn = true;
}else{
$scope.isLoggedIn = false;
}
}
The logout function is called when the log out button is pressed. What am I doing wrong here?
Note: The application is not working on Chrome for some reason (Use Firefox). If you can shed some light on that, it would be very helpful.
Both the repositories are public if you wish to have a look:
AngulAir Application: http://gitlab.learningtechasia.com:8901/rohan0793/angulair.git
AngulAirAPI: http://gitlab.learningtechasia.com:8901/rohan0793/angulairapi.git
Here is your solution
$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete(
{'auth_token': $cookieStore.get('auth_token')}, // parameters
{},//postData, which you don't need for this
function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
},
// error callback
function (httpResponse) {
// do what you want for error handling here
}
);
};
Note:-> (Below points solved the problem)
Only the 2nd option(postdata) in $resource.delete API was missing. We should give it as a blank {} if it is not required for API.
And delete method should return 204 Status Code in order to execute success callback.

Categories