React - How to check if JWT is valid before sending a post request?
Solution 1
JWT expiration can be checked in two ways. First of all you have to install jsonwebtoken package and require it at the top of your file. Thereafter, you can follow the below ways to check JWT expiration before sending any rest requests.
Option 1
var isExpired = false;
const token = localStorage.getItem('id_token');
var decodedToken=jwt.decode(token, {complete: true});
var dateNow = new Date();
if(decodedToken.exp < dateNow.getTime())
isExpired = true;
Option 2
const token = localStorage.getItem('id_token');
jwt.verify(token, 'shhhhh', function(err, decoded) {
if (err) {
/*
err = {
name: 'TokenExpiredError',
message: 'jwt expired',
expiredAt: 1408621000
}
*/
}
});
Check the error of that method. If it is the TokenExpiredError then that means the token is expired.
Solution 2
Here is a solution with jwt-decode library by comparing the exp attributes in the JWT token with current time. (JWT token is simply a Base64 encoded string)
-
Install jwt-decode (
npm install jwt-decode --save
)let token = localStorage.getItem(TOKEN); let decodedToken = jwt_decode(token); console.log("Decoded Token", decodedToken); let currentDate = new Date(); // JWT exp is in seconds if (decodedToken.exp * 1000 < currentDate.getTime()) { console.log("Token expired."); } else { console.log("Valid token"); result = true; }
IMPORTANT: jwt-decode doesn't validate the token, any well formed JWT can be decoded. You should validate the token in your server-side logic by using something like express-jwt, koa-jwt, Owin Bearer JWT, etc.
Solution 3
You could also use a middleWare to check if the token has been expired. You even could update the token if it will almost expire. For example, you could do something shown below;
export function jwtMiddleware({ dispatch, getState }) {
return (next) => (action) => {
switch (action.type) {
case 'CHECK_AUTH_TOKEN' :
if (getState().auth && getState().auth.token) {
var tokenExpiration = jwtDecode(getState().auth.token).exp;
var tokenExpirationTimeInSeconds = (tokenExpiration - moment(Math.floor(Date.now() / 1000)));
if (tokenExpiration && tokenExpirationTimeInSeconds < 20) {
history.push(i18next.t('translation:routes.auth.logout'));
}
}
break;
case 'UPDATE_AUTH_TOKEN' :
if (getState().auth && getState().auth.token) {
var tokenExpiration = jwtDecode(getState().auth.token).exp;
var tokenExpirationTimeInSeconds = (tokenExpiration - moment(Math.floor(Date.now() / 1000)));
if (tokenExpiration && tokenExpirationTimeInSeconds < 100 && tokenExpirationTimeInSeconds > 20) {
if (!getState().auth.fetching) {
return dispatch(refreshAuthToken(getState().auth));
}
}
}
break;
case 'REFRESH_AUTH_TOKEN_FAIL' :
if (getState().auth && getState().auth.token) {
return dispatch(removeAuthToken(getState().auth)).then(response => {
history.push(i18next.t('translation:routes.auth.logout'));
});
}
break;
}
return next(action);
}
}
Related videos on Youtube
Samia Ruponti
SOreadytohelp !! I'm now a full time front end developer! I love to write clean code, enjoy petting our office dog and I am currently learning (a bit late though) about SVGs!
Updated on November 29, 2020Comments
-
Samia Ruponti over 3 years
another noob question. I'm logging in my user to the system using JWT authorization, getting the token and saving it in
localstorage
and then sending a post request that saves data (its a big form basically). Problem is, the sever is invalidating the token after a given time (20 minutes or so) and so, some of my post requests are returning401 status
. How to verify (and if needed, show a login prompt) before sending the post request? I'm usingredux-form
to make my forms.P.S: I know I'm supposed to use action creators and such, but I'm still a newbie, so not very good at those stuff.
here's my authentication:
export function loginUser(creds) { const data = querystring.stringify({_username: creds.username, _password: creds.password}); let config = { method: 'POST', headers: { 'Content-Type':'application/x-www-form-urlencoded' }, body: data }; return dispatch => { // We dispatch requestLogin to kickoff the call to the API dispatch(requestLogin(creds)); return fetch(BASE_URL+'/login_check', config) .then(response => response.json().then(user => ({ user, response })) ).then(({ user, response }) => { if (!response.ok) { // If there was a problem, we want to // dispatch the error condition dispatch(loginError(user.message)); return Promise.reject(user) } else { // If login was successful, set the token in local storage localStorage.setItem('id_token', user.token); let token = localStorage.getItem('id_token') console.log(token); // Dispatch the success action dispatch(receiveLogin(user)); } }).catch(err => console.log("Error: ", err)) } }
and here's the
POST
request (I'm getting thevalues
object fromredux-form
)const token = localStorage.getItem('id_token'); const AuthStr = 'Bearer '.concat(token); let headers ={ headers: { 'Content-Type':'application/json','Authorization' : AuthStr } }; export default (async function showResults(values, dispatch) { axios.post(BASE_URL + '/new', values, headers) .then(function (response) { console.log(values); console.log(response); }) .catch(function (error) { console.log(token); console.log(values) console.log(error.response); }); });
P.P.S: if anyone has any suggestion for improving my code, feel free to comment.
-
Pi Home Server about 6 yearsAre you pretty sure about the solution 2 ? That means that the front will have the secret key to check the token. Then looking at the source code, you can find it and use it to generate valide tokens
-
Suvethan Nantha about 6 years@PiHomeServer Yes actually based on the question asked I answered and gave two ways of fixing it. But having said that we can still improve the way we check the JWT expiration.
-
Suvethan Nantha about 6 years@SamiaRuponti if the answer I provided is correct for your situation can you please upvote my answer? If it's not perfect,I'm also learning so tell me what went wrong. Thanks.
-
Samia Ruponti about 6 yearsI actually followed neither option. As Pi Home Server said, that's not very secure, so ended up doing some server-side validation thing. Thank you though.
-
Suvethan Nantha about 6 years@SamiaRuponti Yes of course, server side validations are always secure, as per the question I answered you. Anyway thanks for the feedback.
-
Rares over 4 yearsAs per RFC 7519,
exp
claim is aNumericDate
value, which is defined as a number of seconds.Date
'sgetTime()
method returns milliseconds. -
Someone Special almost 4 years@SamiaRuponti it is not insecure to decode JWT on client side. Anybody who have your JWT token can decode it. There's even online decoder you can use. Thats why you only store insecure information like userid, expiration date etc. Method one is usable. Method 2 is not.
-
Brian Sizemore almost 4 years@SamiaRuponti Just to clarify for you about JWT tokens. JWT tokens are simply base64 encoded so anyone can "decode" the token to see what claims are present within the token. jwt.io This site is a great resource for exploring that. The third section of a JWT is the signature, which is signed and verified only using the secret key stored on the server. The secret key should always be kept safe, but theoretically you could simply check the exp (expiration) header within the JWT without needing to communicate with the server.
-
Samia Ruponti almost 4 yearsThank you. this is a very old question though.
-
PravyNandas almost 4 yearsThanks for Option1. The only update I had to make is to convert the exp to millis (*1000) or reduce .getTime() by 1000 to do comparison. Not sure if somebody have this similar issue.
-
wonsuc about 3 yearsI think this is more valid than the accepted answer.
jwt-decode
is made by the same author ofjsonwebtoken
which is being mentioned in the accepted answer and if you are going to onlydecode
(and you have to) thetoken
from the browser(client-side), this library is made for that purpose.