refresh JWT token when expire time is reached - react-native

I'm working on a React native application and I need to set Authentication header on my sensitive requests.
But my accessToken (JWT) expires after 10 seconds. So before any request I have to check that if the token is expired renew it using a refreshToken and then call last request again.
I'm able to do all of these except last part (bold text).
And I'm using Axios for sending request to server.
Any idea?

So the idea is to use a response interceptor that works after the response is available, but before it is passed down in the code.
It looks for unauthenticated error, which corresponds to statusCode 401
Keep in mind that this is a pseudo-code and You have to modify some parts like
auth.setToken().
const createAxiosResponseInterceptor = () => {
const interceptor = axios.interceptors.response.use(
(response) => response,
(error) => {
// Reject promise if not 401 error
if (error.response.status !== 401) {
return Promise.reject(error);
}
/*
* When response code is 401, try to refresh the token.
* Remove the interceptor so it doesn't loop in case
* token refresh causes the 401 response
*
* Also eject the interceptor to prevent it from working again if the REFRESH request returns 401 too
*/
axios.interceptors.response.eject(interceptor);
return axios({
url: API_REFRESH_URL,
method: "POST",
withCredentials: true,
})
.then((response) => {
auth.setToken(response.data.access_token);
error.response.config.headers["Authorization"] =
"Bearer " + response.data.access_token;
return axios(error.response.config);
})
.catch((error) => {
auth.removeToken();
return Promise.reject(error);
})
.finally(createAxiosResponseInterceptor);
}
);
};
createAxiosResponseInterceptor();
error.response.config contains all the data about the old request so we can repeat it. Keep in mind that after completing the Refresh request we again apply the interceptor in .finally(create...Interceptor)
For more details please see this question, and official Docs here

Related

401 Unauthorized error in Express API post request

I am trying to develop the logic for a POST route handler in Express. I put the following together:
const headers = {
"Authorization":"TS Token asdfghjk-asdf-4567-fghjkl; tid=onfido-token";
"content-type": "application/json"
};
const params = {
"policy_request_id": "onfido_applicantandtoken"
};
app.get("/get_stuff", (req, res) => {
axios
.post("https://third/party/api", {
headers,
params
})
.then(function (response) {
res.json(response.data);
})
.catch(function (error) {
res.json("Error occured!");
});
}
});
I keep getting a 401 Unauthorized for the above. On Postman it works, but with the logic above I get a 401 Unauthorized, specifically in the logs I would get Header 'Authorization' not found or Could not parse authorization header. So I am unclear as to what could be going on with the header.
A lot of posts talk about req.headers, but my req.headers does not have the Authorization token and content-type in there, it has some other token that the API I am trying to connect to I assume needs to reach out to another API.
I have refactored it to look like this:
app.get("/get_stuff", (req, res) => {
axios
.post("https://third/party/api", params, headers)
.then(function (response) {
res.json(response.data);
})
.catch(function (error) {
res.json("Error occured!");
});
}
});
And I am still getting the same exact error.
To be clear the params is not something that gets passed into the URL on Postman, but rather the body of the postman request.
I was able to get it to successfully connect by declaring a global axios default like so:
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
as documented here:
https://www.npmjs.com/package/axios#user-content-config-defaults

How to implement silent token refresh with axios request interceptor?

We are implementing a token-based authentication and when a user signs in we generate access and refresh tokens then save that with the timestamp on device so we can later check if the access token is expired or not.
We are currently using axios interceptor before every request and checking if the token is still valid or not with the timestamp we saved earlier when we generated the access and refresh tokens, but when the access token is expired and we are making a request to refresh the token the app goes on an infinite loop and none of the requests go through (both the original and refresh token api requests). you can see my code below:
const instance = axios.create({
baseURL: 'http://localhost:8080'
});
const refreshToken = () => {
return new Promise((resolve, reject) => {
instance
.post('/token/renew')
.then(response => {
resolve('refresh successful');
})
.catch(error => {
reject(Error(`refresh fail: ${error}`));
});
});
};
instance.interceptors.request.use(
async config => {
const timestamp = 1602155221309;
const diffMinutes = Math.floor(Math.abs(Date.now() - timestamp) / 60000);
// if diffMinutes is greater than 30 minutes
if (diffMinutes > 30) {
const tokenResponse = await refreshToken();
return config;
}
return config;
},
error => {
return Promise.reject(error);
}
);
The infinite loop is caused by your interceptor triggering another Axios request on which said interceptor will also run and trigger another Axios request, forever.
A simple solution would be to make the refresh token network request using the default axios instance which doesn't include any interceptors:
const refreshToken = () => {
// You can skip returning a new `Promise` since `axios.post` already returns one.
return axios.post("YOUR_BASE_URL/token/renew");
};
Obviously that also means you'll have to write a bit of logic to send the current refresh token along if that's included in your instance interceptors.

Vue axios doesnt change header

I am quite new to vue and I am trying to send a request to my api using axios.
I build an interceptor which seems to work (logging is happening)
export default function setup() {
console.log('Http interceptor starting...')
Axios.interceptors.request.use((request) => {
const token = store.getters.token;
if (token) {
request.headers.Authorization = `Bearer ${token}`;
}
console.log(request);
return request
}, (err) => {
return Promise.reject(err);
});
}
If I check the console I can see the request including the token. If I check my network tab in the browser i can see the same request without the token. If I check the console of my api the token is null. Any Ideas?
Edit: If I use postman with the same request and the same token it is working as it shuld

Excessive number of api calls for refresh token

After reading through multiple JWT refresh token tutorials I am still unclear on how the flow of API calls is supposed to work.
Here is my understanding:
1) Client is in posession of access token and refresh token. It submits the access token to api/getCustomerData.
2) Let's say the access token is expired. Server responds with a 401.
3) Client responds to the 401 request with the refresh token to api/token.
4) Server responds with new access token since the refresh token is still valid.
5) Client then makes a new request to api/getCustomerData with the new access token.
My impression is that this is an excessive number of API calls, but I am not seeing any tutorial that clarifies a way to do this more efficiently. As it stands it seems like if I am following this pattern, each API request will look like this:
const getCustomers = async () => {
const config = {
data: body,
withCredentials: true,
method: 'POST' as 'POST',
}
await axios(address + '/api/getCustomerData', config)
.then((response) => {
...
})
.catch((error: any) => {
const response = error.response;
if (response) {
if (response.status === 401) {
if (!failcount){
failcount++;
getCustomers();
}
else {
history.push('/login')
}
}
}
})
}
What you can do is pre-emptively get a new access token using a refresh token shortly before you know the current access token is about to expire, using the token's "exp (expiration time)" claim. That at least removes one API call - the call with the access token that causes a 401.

Accessing the response from one GET request within another

I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}