Axios- redirect in issue - axios

I'm having trouble getting redirects to work after accepting a get request from Axios. I do know that the request is being sent and that it at least gets some response from the URL route,
when i console response.data.redirect it return undefined
const onSubmitHandler = (e) => {
e.preventDefault()
axios.get('/get/user')
.then(function (response) {
if (response.data.redirect == '/' || response.data.redirect == '/login' ) {
window.location = "/login"
} else {
console.log(response.data)
}
})
.catch(function(error) {
window.location = "/login"
})
}
I received status code 302 but it automatically redirects to the page without page refresh shows the redirected page on the same page in a div section.
Anyone knows how can I use interceptor with Axios such that if the status code fall in 200 series then does something else page refresh. I checked the Axios docs but there is no implementation

Try this for interceptors
axios.interceptors.response.use(
response => {
return response.data;
},
err => {
//return new Promise((resolve, reject) => {
//err.response.status for getting error status
// }
throw err;
});
}
);

Related

Service worker returning Offline page instead of 404 page when non-existant file is requested

I'm using this service worker for caching and offline mode.
when I am already on any existing page of my website and I request a non-existent page, the Offline page is served instead of the 404 page.
If, on the other hand, I close and reopen the browser and immediately request a non-existent page of my website, the 404 page is served correctly.
I suspect it has to do with these last few lines of the code
if(!matching || matching.status == 404) { return cache.match("/service/offline/"); but i don't know how to fix this problem. Can you help me please?
Here's the full service worker:
self.addEventListener("install", function(event) {
event.waitUntil(preLoad());
});
var preLoad = function(){
console.log("Installing web app");
return caches.open("offline").then(function(cache) {
console.log("caching index and important routes");
return cache.addAll([
'/assets/css/about.min.css',
'favicon.ico',
'/assets/js/script.js',
'manifest.webmanifest.webmanifest',
'/',
'/about/',
'/contact/',
'/service/offline/'
]);
});
};
self.addEventListener("fetch", function(event) {
event.respondWith(checkResponse(event.request).catch(function() {
return returnFromCache(event.request);
}));
event.waitUntil(addToCache(event.request));
});
var checkResponse = function(request){
return new Promise(function(fulfill, reject) {
fetch(request).then(function(response){
if(response.status !== 404) {
fulfill(response);
} else {
reject();
}
}, reject);
});
};
var addToCache = function(request){
return caches.open("offline").then(function (cache) {
return fetch(request).then(function (response) {
console.log(response.url + " was cached");
return cache.put(request, response);
});
});
};
var returnFromCache = function(request){
return caches.open("offline").then(function (cache) {
return cache.match(request).then(function (matching) {
if(!matching || matching.status == 404) {
return cache.match("/service/offline/");
} else {
return matching;
}
});
});
};

how to go from axios interceptor to then

this is my code
axios
.post("/api/addcart", body)
.then((response) => {
if (response.data.success) {
alert("success");
} else {
alert(response.data.message);
console.log(response.data);
}
}) .catch(() => {
console.log("catch");
});
Created a logic that re-requests when JWT expires with axios interceptor
axios.interceptors.response.use(
function (response) {
return response;},
async function (error) {
const originalRequest = error.config;
console.log(error.response.status);
if (error.response.status === 406) {
axios.post("/api/reissue").then((response) => {
console.log(response.data.success);
if (response.data.success) {
originalRequest._retry = true;
return axios(originalRequest);
} //Removed irrelevant code for readability
return;
// return axios(error.config);
}
return Promise.reject(error);
}
);
The problem I'm having here is after the reissue(jwt token renewal request) is successful.
406 is the response code given when the token expires (I set it temporarily)
I want the alert(success) to be generated by going back to the code above after the access token is updated But this request seems to go to catch
Going back to the beginning, I want to renew if the token has expired and go back to then if the renewal request is successful, but I don't know how. Please let me know if there is a better way

Possible to return 401 instead of 403 for expired token?

I want to be able to return a 401 on JWT token expiry (which I believe is the correct response?) but it returns a 403 no matter what
here is where I tell swagger tools to use my bearer token verification logic:
swaggerTools.initializeMiddleware(swaggerObject, (middleware) => {
// Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
app.use(middleware.swaggerMetadata());
//enable cors
app.use(
cors({
methods: ["GET", "POST", "PATCH", "DELETE"],
origin: "*",
preflightContinue: false,
optionsSuccessStatus: 204,
})
);
//verify bearer token for protected apis
app.use(
middleware.swaggerSecurity({
Bearer: (req, authOrSecDef, token, callback) =>
//if this is a protected endpoint then token is verified to allow or deny access
securityService.verifyToken(
req,
authOrSecDef,
token,
callback
),
})
);
and here is my verifyToken function..
verifyToken: (req, authOrSecDef, token, callback) => {
const sendError = (error) => {
if (error) {
if (error.name === "TokenExpiredError") {
return new Problem(401, error.message);
}
} else {
return new Problem(403);
}
};
if (token && token.toLowerCase().indexOf("bearer ") === 0) {
var tokenString = token.split(" ")[1];
let completeDecodedToken = jwt.decode(tokenString, { complete: true });
if (!completeDecodedToken) {
return callback(sendError());
}
//use header.kid to find correct cognito jwk to use
let jwk = jwks.keys.find(
(jwk) => jwk.kid == completeDecodedToken.header.kid
);
if (!jwk) {
//must have passed in a token obtained from somewhere else
return callback(sendError());
}
const pem = jwkToPem(jwk);
jwt.verify(
tokenString,
pem,
{ algorithms: ["RS256"] },
(verificationError, decodedToken) => {
if (verificationError === null) {
// check if the issuer matches
var issuerMatch =
decodedToken.iss ===
`https://cognito-idp.${process.env.AWS_REGION}.amazonaws.com/${process.env.COGNITO_USER_POOL_ID}`;
if (issuerMatch) {
//add the token to the request so that we
//can access it downstream in endpoint if we need
req.auth = decodedToken;
req.tokenString = tokenString;
//if there is no error, just return null in the callback
return callback(null);
} else {
console.error("Issuer did not match");
//return the error in the callback if there is one
return callback(sendError());
}
} else {
//return the error in the callback if the JWT was not verified
return callback(sendError(verificationError));
}
}
);
} else {
return callback(sendError());
}
},
but actually when I look in the swagger-tools source (swagger-security.js) I only see 403 in there.. any advice ?
You see 403 always because that is the else part and your error object is null, If you are able to debug then you can console log the error
Also your assumption is that error is returned from below line, which is highly likely wrong because error is null.
//return the error in the callback if the JWT was not verified
return callback(sendError(verificationError));
In my opinion, error is returned from
else {
return callback(sendError());
}
If that is the case then you can send your custom "UnauthorizedError" object from the desired place.

How to get axios error response INTO the redux saga catch method

With axios the code is:
export const createBlaBla = (payload) => {
return axios.post('/some-url', payload)
.then(response => response)
.catch(err => err);
}
And then I'm using this with redux-saga like this:
function* createBlaBlaFlow(action) {
try {
const response = yield call(createBlaBla, action.payload);
if (response) {
yield put({
type: CREATE_BLA_BLA_SUCCESS
});
}
} catch (err) {
// I need the error data here ..
yield put({
type: CREATE_BLA_BLA_FAILURE,
payload: 'failed to create bla-bla'
});
}
}
In case of some error on the backend - like invalid data send to the backend - it returns a 400 response with some data:
{
"code":"ERR-1000",
"message":"Validation failed because ..."
"method":"POST",
"errorDetails":"..."
}
But I don't receive this useful data in the catch statement inside the saga. I can console.log() the data in the axios catch statement, also I can get it inside the try statement in the saga, but it never arrives in the catch.
Probably I need to do something else? ... Or the server shouldn't return 400 response in this case?
So, I came up with two solutions of this problem.
===
First one - very dump workaround, but actually it can be handy in some specific cases.
In the saga, right before we call the function with the axios call inside, we have a variable for the errors and a callback that sets that variable:
let errorResponseData = {};
const errorCallback = (usefulErrorData) => {
errorResponseData = usefulErrorData;
};
Then - in the axios method we have this:
export const createBlaBla = (payload, errCallback) => {
return axios.post('/some-url', payload)
.then(response => response)
.catch(err => {
if (err && err.response.data && typeof errCallback === 'function') {
errCallback(err.response.data);
}
return err;
});
}
This way, when we make request and the backend returns errors - we'll call the callback and will provide the errors from the backend there. This way - in the saga - we have the errors in a variable and can use it as we want.
===
However, another solution came to me from another forum.
The problem I have is because in the method with the axios call I have catch, which means that the errors won't bubble in the generator. So - if we modify the method with the axios call like this:
export const createBlaBla = (payload) => {
return axios.post('/some-url', payload)
}
Then in the catch statement in the saga we'll have the actual backend error.
Hope this helps someone else :)
In your API call you can do the following:
const someAPICall = (action) => {
return axios.put(`some/path/to/api`, data, {
withCredentials: true,
validateStatus: (status) => {
return (status == 200 || status === 403);
}
});
};
Please note the validateStatus() part - this way when axios will encounter 200 or 403 response, it will not throw Error and you will be able to process the response after
const response = yield call(someAPICall, action);
if (response.status === 200) {
// Proceed further
} else if (response.status === 403) {
// Inform user about error
} else {
...
}

How to catch a 401 (or other status error) in an angular service call?

Using $http I can catch errors like 401 easily:
$http({method: 'GET', url: 'http://localhost/Blog/posts/index.json'}).
success(function(data, status, headers, config) {
$scope.posts = data;
}).
error(function(data, status, headers, config) {
if(status == 401)
{
alert('not auth.');
}
$scope.posts = {};
});
But how can I do something similar when using services instead. This is how my current service looks:
myModule.factory('Post', function($resource){
return $resource('http://localhost/Blog/posts/index.json', {}, {
index: {method:'GET', params:{}, isArray:true}
});
});
(Yes, I'm just learning angular).
SOLUTION (thanks to Nitish Kumar and all the contributors)
In the Post controller I was calling the service like this:
function PhoneListCtrl($scope, Post) {
$scope.posts = Post.query();
}
//PhoneListCtrl.$inject = ['$scope', 'Post'];
As suggested by the selected answer, now I'm calling it like this and it works:
function PhoneListCtrl($scope, Post) {
Post.query({},
//When it works
function(data){
$scope.posts = data;
},
//When it fails
function(error){
alert(error.status);
});
}
//PhoneListCtrl.$inject = ['$scope', 'Post'];
in controller call Post like .
Post.index({},
function success(data) {
$scope.posts = data;
},
function err(error) {
if(error.status == 401)
{
alert('not auth.');
}
$scope.posts = {};
}
);
Resources return promises just like http. Simply hook into the error resolution:
Post.get(...).then(function(){
//successful things happen here
}, function(){
//errorful things happen here
});
AngularJS Failed Resource GET
$http is a service just like $resource is a service.
myModule.factory('Post', function($resource){
return $http({method: 'GET', url: 'http://localhost/Blog/posts/index.json'});
});
This will return the promise. You can also use a promise inside your factory and chain that so your factory (service) does all of the error handling for you.