115

I've configured the token like this:

jwt.sign(
  {
    user: pick(user, ['_id', 'username'])
  },
  secret,
  {
    expiresIn: '2m'
  }
);

But when I want to check if the token was expired, this code doesn't work:

function isAuthenticated() {
  const token = localStorage.getItem('token');
  const refreshToken = localStorage.getItem('refreshToken');
  try {
    decode(token);
    const { exp } = decode(refreshToken);
    if (exp < (new Date().getTime() + 1) / 1000) {
      return false;
    }
  } catch (err) {
    return false;
  }
  return true;
}

The problem is this part:

if (exp < (new Date().getTime() + 1) / 1000) {
  return false;
}

new Date().getTime() + 1) / 1000 = 1531335468.113

exp = 1531334595

Because I don't know what format of time JWT uses...

How can I resolve this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrés Montoya
  • 4,079
  • 4
  • 18
  • 33
  • print the output of "new Date().getTime() + 1" and please post. – LearningToCode Jul 11 '18 at 18:53
  • 1
    How are you decoding the token? If you're using a library, it'll validate the expiration of the token automatically (by default). E.g. jsonwebtoken: https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback – Ramon Snir Jul 11 '18 at 18:55
  • @RamonSnir with the library jsonwebtoken – Andrés Montoya Jul 11 '18 at 18:59
  • 1
    @AndrésMontoya why not use jwt.verify, instead of jwt.decode? jwt.decode doesn't even verify that the token is signed correctly. – Ramon Snir Jul 11 '18 at 19:01
  • @RamonSnir Oh, well, I wanted to know how can I do that with decode xD – Andrés Montoya Jul 11 '18 at 19:05
  • 1
    @RamonSnir I'm using jwt-decode on the browser and jsonwebtoken on the server, the function isAuthenticated is from the client – Andrés Montoya Jul 11 '18 at 19:30
  • The code looks fine to me, as do the numbers. can you be more precise than "doesn't work"? also, JWT stores times in seconds since epoch. https://tools.ietf.org/html/rfc7519#page-6 – Kevin Burdett Jul 11 '18 at 20:11

11 Answers11

211

This is the answer:

if (Date.now() >= exp * 1000) {
  return false;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrés Montoya
  • 4,079
  • 4
  • 18
  • 33
50

You should use jwt.verify. It will check if the token is expired.

jwt.decode should not be used if the source is not trusted as it doesn't check if the token is valid.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gabriel Bleu
  • 9,703
  • 2
  • 30
  • 43
  • The function isAuthenticated works in the front-end.Anyway the solution was commented for me... – Andrés Montoya Jul 11 '18 at 20:29
  • 3
    Oh ok but you don't receive the `expiresIn` with the token ? – Gabriel Bleu Jul 11 '18 at 20:44
  • But what is the purpose of `exp`? – Shift 'n Tab Oct 23 '19 at 11:24
  • @Roel exp is used in the frontend, it's used to set a timeout and log out the user or otherwise refresh the token. I didn't know we could simply use jwt.verify to check if the token has expired. It's stated in the docs that: "The callback is called with the decoded payload if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will be called with the error.", so we could simply use if (jwt.verify(token, secret) to verify if it has expired. – Herii Aug 28 '21 at 22:45
41

Function without the jwt library:

Browser

function isTokenExpired(token) {
  const base64Url = token.split(".")[1];
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
  const jsonPayload = decodeURIComponent(
    atob(base64)
      .split("")
      .map(function (c) {
        return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
      })
      .join("")
  );

  const { exp } = JSON.parse(jsonPayload);
  const expired = Date.now() >= exp * 1000
  return expired
}

Or simpler

function isTokenExpired(token) {
  const expiry = (JSON.parse(atob(token.split('.')[1]))).exp;
  return (Math.floor((new Date).getTime() / 1000)) >= expiry;
}

Or a one-liner:

const isTokenExpired = token => Date.now() >= (JSON.parse(atob(token.split('.')[1]))).exp * 1000

Node.js

function isTokenExpired(token) {
  const payloadBase64 = token.split('.')[1];
  const decodedJson = Buffer.from(payloadBase64, 'base64').toString();
  const decoded = JSON.parse(decodedJson)
  const exp = decoded.exp;
  const expired = (Date.now() >= exp * 1000)
  return expired
}

Or a one-liner:

const isTokenExpired = (token) => (Date.now() >= JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).exp * 1000)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Emanuel
  • 2,603
  • 22
  • 24
17

verify itself returns an error if expired. It is safer as Gabriel said.

const jwt = require('jsonwebtoken')

router.use((req, res, next) => {
  const token = yourJwtService.getToken(req) // Get your token from the request
  jwt.verify(token, 'your-secret', function(err, decoded) {
    if (err) throw new Error(err) // Manage different errors here (Expired, untrusted...)
    req.auth = decoded // If no error, token info is returned in 'decoded'
    next()
  });
})
Rashomon
  • 5,962
  • 4
  • 29
  • 67
8

Sadly, Andrés Montoya's answer has a flaw which is related to how he compares the object.

I found a solution here which should solve this:

const now = Date.now().valueOf() / 1000

if (typeof decoded.exp !== 'undefined' && decoded.exp < now) {
    throw new Error(`token expired: ${JSON.stringify(decoded)}`)
}
if (typeof decoded.nbf !== 'undefined' && decoded.nbf > now) {
    throw new Error(`token expired: ${JSON.stringify(decoded)}`)
}

Thanks to user thejohnfreeman!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lony
  • 6,733
  • 11
  • 60
  • 92
5

The following function works without any libraries:

function getJWTExpireDate(jwtToken: string) {
  if (jwtToken) {
    try {
      const [, payload] = jwtToken.split('.');
      const { exp: expires } = JSON.parse(window.atob(payload));
      if (typeof expires === 'number') {
        return new Date(expires * 1000);
      }
    } catch {
      // ignore
    }
  }
  return null;
}

Don't use this to check whether the token is valid. One good use case is displaying when the token expires in the frontend.

Sandro
  • 1,051
  • 7
  • 15
5

Client side

const JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyMzQ1Njc4OTAsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0.1c_yQjnMZfKUb4UTDE_WvbC71f8xxtyMsdqKKkI1hF8";

const jwtPayload = JSON.parse(window.atob(JWT.split('.')[1]))
const isExpired = Date.now() >= jwtPayload.exp * 1000;

Bambier
  • 586
  • 10
  • 16
1

some answers omit verification step which is super dangerous!

safe replacement for jwt.verify with following changes

  • it does NOT throw when token is expired, but does throw for invalid token.
  • return object is {payload, expired} instead of payload

js version

export function verifyJWT(token) {
  try {
    return { payload: jwt.verify(token, publicKey), expired: false };
  } catch (error) {
    if (error.name == "TokenExpiredError") {
      return { payload: jwt.decode(token), expired: true };
    }
    throw error;
  }
}

ts version

export function verifyJWT(token: string) {
  try {
    return { payload: jwt.verify(token, publicKey), expired: false };
  } catch (error) {
    if ((error as Error).name == "TokenExpiredError") {
      return { payload: jwt.decode(token), expired: true };
    }
    throw error;
  }
}

Ali80
  • 6,333
  • 2
  • 43
  • 33
0

This is for React Native, but login will work for all types.

isTokenExpired = async () => {
    try {
        const LoginTokenValue = await AsyncStorage.getItem('LoginTokenValue');
        if (JSON.parse(LoginTokenValue).RememberMe) {
            const { exp } = JwtDecode(LoginTokenValue);
            if (exp < (new Date().getTime() + 1) / 1000) {
                this.handleSetTimeout();
                return false;
            } else {
                //Navigate inside the application
                return true;
            }
        } else {
            //Navigate to the login page
        }
    } catch (err) {
        console.log('Spalsh -> isTokenExpired -> err', err);
        //Navigate to the login page
        return false;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gauravsbagul
  • 79
  • 1
  • 2
  • 8
0

You can use the jwt verify method to check the validity of your token. err means the token is expired and will throw an error so you can remove the stored token.

 jwt.verify(token, SECRET, (err, decoded) => {
      if (err) {
          localStorage.clear();
      }}
  });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sehrish Waheed
  • 1,230
  • 14
  • 17
  • 1
    Hi, how is it possible to remove that specific token for that user , since the information for getting the user is stored in the token which I can't get since it throws an error ? – Nathaniel Babalola Jul 06 '21 at 11:33
-4
// Pass in function expiration date to check token 
function checkToken(exp) {
    if (Date.now() <= exp * 1000) {
      console.log(true, 'token is not expired')
    } else { 
      console.log(false, 'token is expired') 
    }
  }
Zaur
  • 1