React - How to check if JWT is valid before sending a post request?

42,707

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);
  }
}
Share:
42,707

Related videos on Youtube

Samia Ruponti
Author by

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, 2020

Comments

  • Samia Ruponti
    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 returning 401 status. How to verify (and if needed, show a login prompt) before sending the post request? I'm using redux-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 the values object from redux-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
    Pi Home Server about 6 years
    Are 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
    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
    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
    Samia Ruponti about 6 years
    I 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
    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
    Rares over 4 years
    As per RFC 7519, exp claim is a NumericDate value, which is defined as a number of seconds. Date's getTime() method returns milliseconds.
  • Someone Special
    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
    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
    Samia Ruponti almost 4 years
    Thank you. this is a very old question though.
  • PravyNandas
    PravyNandas almost 4 years
    Thanks 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
    wonsuc about 3 years
    I think this is more valid than the accepted answer. jwt-decode is made by the same author of jsonwebtoken which is being mentioned in the accepted answer and if you are going to only decode(and you have to) the token from the browser(client-side), this library is made for that purpose.