-1

I tried to create a get timestamp function like Date.now(). I assume that Date.now() will use the time from the user's computer, so there is no guarantee that the UNIX time is accurate if the user sets the time manually. I would create a function to get a standardized timestamp from the time server API instead, to make sure that the timestamp is the same for all of the users.

function timefromInternet() {
    return new Promise((resolve, reject) => {
        fetch("http://worldtimeapi.org/api/timezone/Asia/Taipei")
            .then(response => response.json())
            .then(data => {
                resolve(data.unixtime);
        }).catch(error => { resolve(Date.now()); });
    });
}

but it is too slow, so I could not execute like Date.now() for example like this:

let callInfo = {
    timestamp: Date.now(),
    status: "PAUSE",
};

this.$store.commit("setCallInfo", callInfo);
this.$store.commit("updateLocalUserByObject", {
    status: callInfo.status,
});

I want to replace Date.now() with something like this:

let callInfo = {
    timestamp: timefromInternet(),
    status: "PAUSE",
};

this.$store.commit("setCallInfo", callInfo);
this.$store.commit("updateLocalUserByObject", {
    status: callInfo.status,
});

What is the best solution to modify timefromInternet() so it could be run like Date.now()? Because if I am using promises, I could not call like Date.now() above. Thanks in advance.

3 Answers3

2

You can request the time only once at page load and then make your getTimeStamp() synchronous.
There are a few clock timers available in the browser, some that the users can't easily mess with. For instance performance.now() will be relative to the page load and will use a monotonic clock which should keep ticking, no matter what the user does with its System Settings (contrarily to Date, which in some UAs always request the system date, not only at page load).

One case where this clock may stop ticking is when the computer goes to sleep. One workaround for that is to make a new request when the page gains focus again.

async function fetchExactDateFromServer() {
  const resp = await fetch("https://worldtimeapi.org/api/timezone/Europe/Paris");
  if (!resp.ok) {
    throw new Error("Network Error");
  }
  return +new Date((await resp.json()).datetime);
}
let offset = 0;
function getTimeStamp() {
  return Math.round(performance.now() - offset);
}
function updateTime() {
  return fetchExactDateFromServer()
    .then((officialTime) => {
      offset = performance.now() - officialTime;
    })
    .catch((err) => {
      console.error(err);
      return Promise.reject(err);
    });
}
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === 'visible') {
    updateTime();
  }
});
// test by logging the difference between server-side and local (should always be about the same difference)
const btn = document.querySelector("button");
updateTime().then(() => {
  btn.onclick = (evt) => console.log(getTimeStamp(), Date.now(), getTimeStamp() - Date.now());
  btn.disabled = false;
});
<button disabled>Test</button>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
2

I think that after reading your comments (in regard to protecting against free trial abuses) you're approaching this in logically the wrong way. Even with a super-efficient way of pulling an accurate timestamp from the Internet in some way, it is actually trivial for anyone to simply replace the function/return locally in their browser, and so defeat your test. They don’t even need to supply a timestamp, they can just replace the function with something that returns TRUE for every test. Why? Because anything you test in the client (browser) is open to modification by a reasonably sophisticated user, which means that all validation must be enforced server-side if it is to avoid being circumvented.

As an alternative, simply store the trial period details at the server end (where you have total control over the validity of the timestamp), and if a request from the user is made after the trial has expired, then reject it.

Buffoonism
  • 1,669
  • 11
  • 11
0

You should trust the system clock. Virtually all modern OSen are running an NTP daemon of some flavour or other.

There's too much stuff these days, Kerberos off the top of my head, and SSL/TLS, that depends on clock synchronicity for them to do otherwise.

About the only exception might be systems in a hardened, air-gapped, environment. And they've probably got an accurate time source of their own (GPS receiver, atomic clock radio, etc.)

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • Virtually all modern OS allow the user to set whatever they like as the current date-time. There are many cases where you can't trust it (for instance would you base a 30 days free trial on Date.now()? You'd only have free trial users because it's so easy to tell the browser that today and tomorrow are the first of January. – Kaiido Aug 12 '22 at 05:10
  • A regular connect (such as in a browser etc) via SSL and TLS don't care about clock drift in general, other than validating the certificate has not expired. A shift of a month +/- (to defeat a trial period) is likely to be fine. – Buffoonism Aug 12 '22 at 09:15