3
\$\begingroup\$

Basically, the server gives me a token object. I am planning to check the token on the client if it is expired/valid before making a request to the server.

The function below works as expected. I just want to know if my code catch-all edge cases? I don't want, even 1 second that will request will fail due to an expired access token.

I am using moment for comparing dates. I also converted the current date and expiry date to UTC so that I'm making sure that they are in the same timezone before comparing them.

const moment = require('moment');

/**
The token object passed in the function looks like this:
{
  access_token: '2hbssMdXDpwQX5WcnZ-iJlO754MLkEeDCmF-f1A-MaU',
  token_type: 'Bearer',
  expires_in: 604800,
  refresh_token: 'VxnN9uBVIcNMpuwRVpvXo2YxWuNFEayHqfnCM7aCTSI',
  scope: 'public',
  created_at: 1603604241
}
*/

export default function tokenValid(token = {}) {
  const currentDate = moment().utc();
  const expiryDate = moment.unix(token.created_at).add(token.expires_in, 'seconds').utc();

  return currentDate < expiryDate;
}

EDIT (based on @hjpotter92's comment)

export default function tokenValid(token = {}) {
  const currentDate = moment().unix();
  const expiryDate = token.created_at + token.expires_in;

  return currentDate < expiryDate;
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ epoch (or unix time) is always timezone insensitive. you can treat it as a normal number. \$\endgroup\$
    – hjpotter92
    Commented Oct 25, 2020 at 6:56
  • \$\begingroup\$ @hjpotter92 I have updated the code based on your comment. \$\endgroup\$
    – dcangulo
    Commented Oct 25, 2020 at 7:28

2 Answers 2

4
\$\begingroup\$

Welcome to Code Review!

As I already mentioned in comments, epoch values can be treated as normal numerals, without having to involve momentjs or other external libraries.

In js, you can get current epoch value in milliseconds accuracy using Date.now(). The value in token is with seconds accuracy. It is only a factor of 1000.

Rewrite

/**
The token object passed in the function looks like this:
{
  access_token: '2hbssMdXDpwQX5WcnZ-iJlO754MLkEeDCmF-f1A-MaU',
  token_type: 'Bearer',
  expires_in: 604800,
  refresh_token: 'VxnN9uBVIcNMpuwRVpvXo2YxWuNFEayHqfnCM7aCTSI',
  scope: 'public',
  created_at: 1603604241
}
*/

export default function tokenValid(token = {}) {
  const now = Date.now() / 1000;
  const expiry = token.created_at + token.expires_in;
  return now < expiry;
}

EDIT

In response to the comment:

Changed

// from
parseInt(Date.now() / 1000)
// to
Date.now() / 1000

In the case here, Math.floor or Math.round are more suitable. parseInt is usually called when you want to convert from string to integer. We already know that Date.now() is giving a number and not a string. Also, the conversion/rounding is not really needed here.

\$\endgroup\$
2
  • \$\begingroup\$ I have seen that most use Math.floor() to remove the millisecond in Date.now() instead of parseInt, is there any advantages on using parseInt? \$\endgroup\$
    – dcangulo
    Commented Oct 25, 2020 at 7:47
  • 1
    \$\begingroup\$ I also changed this token.created_at + token.expires_in to token.created_at + token.expires_in - 60, the 60 seconds is for fail-safe. The use case would be if there is only 1 second before the expiration time the client will mark it as valid, but if the request to server took longer that 1 second, it will be expired when it reaches the server. \$\endgroup\$
    – dcangulo
    Commented Oct 25, 2020 at 8:03
5
\$\begingroup\$

You seem to ignore two facts.

  1. The client clock can be out of sync with the server clock.

  2. The transport of the request from client to server takes more then zero time.

Both these cases mean that no matter how hard you try on the client side, there is always a chance that the server will evaluate the token as expired during its processing.

What you should do is have the client take the same action when server refuses the token as you do when your client side validation fails.

In general, client side validation

  • is not necesary
  • is not reliable
  • reduces network traffic
  • increases user experience

Whereas backend side validation

  • is necesary
  • is the only source of truth

Clients should understand and handle possible server side validation errors prior to deploying their own validation logic.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.