Closed nikitadubyk closed 5 months ago
Hi @nikitadubyk !
Are you using queryTime: true
option when initializing your Ably Client? If not, could you please try that and see if that fixes your issue? You can do it like this:
new Ably.Realtime({ ...other options, queryTime: true });
// or if using Rest client
new Ably.Rest({ ...other options, queryTime: true });
By default, the library will use the local computer time to generate the current timestamp and, as a result, the "issued" field in the token. So, the problem may be coming from the clock synchronization issue between your server/client computers and Ably servers. If your clock is ahead by a significant enough difference, you may end up sending to Ably servers a token that was issued in the future.
When using queryTime: true
, the library will query Ably servers for the current time instead of relying on locally available time, thus eliminating any possible synchronization issues.
@VeskeR I tried adding queryTime: true
to Ably.Realtime
as specified in your example and it didn't work, still the error remains
Hey @nikitadubyk , I would need more information about your current setup to troubleshoot this. Could you please provide a code example of how you are initializing your Ably client on the frontend? And a code example of how you are initializing the Ably client on the backend and generating the token there?
Also, previously when mentioning queryTime: true
, I forgot to specify that this option should be passed to whichever Ably client instance is responsible for generating an Ably token. In your case, queryTime: true
should be passed to the Ably client instance that is generating a token on the backend. Passing this option to the Ably client instance on the frontend won't make a difference, since frontend is using a token generated by the backend.
@VeskeR this is how a new Realtime is created on the backend
new Realtime({
queryTime: true,
key: config.apiKey,
clientId: config.clientId,
});
and this is how Realtime is created on the frontend. I also tried adding queryTime: true
to the frontend along with the backend - didn't help.
const ablyToken = request for get ably token;
new Realtime({
logLevel: 2,
clientId: name,
useTokenAuth: true,
token: ablyToken.data,
});
and this is how generated ably token on backend
payload with '*': ['publish', 'subscribe', 'presence'],
jwt.sign(payload, keySecret, jwtOptions, (error, token) => {
if (error) {
logger.error('Ably Token: Could not sign jwt token', error);
next(error);
return;
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(token));
});
@VeskeR I also noticed that with issueAt error this message appears in the console
Hey @nikitadubyk , thank you for providing more information!
Based on the code snippet you provided:
jwt.sign(payload, keySecret, jwtOptions, (error, token) => { ... })
I assume you're using jsonwebtoken module to generate JWT tokens for Ably auth, following this guide from Ably.
jsonwebtoken
automatically includes iat
(issued at) claim upon signing the token, and it is based on the current time on the machine. In your case, it appears that the machine signing those tokens (whether it's your local machine during development or your server) has an inaccurate clock and is running ahead of Ably servers. Thus, tokens are signed with an iat
that is in the future, resulting in the error: Invalid token; issuedAt time must not be in the future
when trying to use this token in your frontend client.
Ideally you should ensure that your server has an accurate clock, using something like NTP daemon.
If this is not something you can change, then you can override the value used for iat
claim by jsonwebtoken
by adding it to your payload object, see example in jsonwebtoken docs%20%2D%2030%20%7D%2C%20%27shhhhh%27)%3B). Your payload will look something like this:
const payload = {
// other props...
iat: Math.floor(Date.now() / 1000) - 5, // backdate by 5 seconds
};
If this fixes the issue for you, it would confirm that the problem arises from an inaccurate clock on your server.
To obtain better timestamps for iat
, you can also calculate the time offset between your server and Ably servers by querying the current time on Ably servers using the https://rest.ably.io/time
endpoint.
You can easily do this using the Ably.Rest
instance like this:
const ablyRest = new Ably.Rest({ key });
const ablyServerTime = await ablyRest.time();
const offset = ablyServerTime - Date.now();
And then use that stored offset when providing iat
in the payload:
const payload = {
// other props...
iat: Math.floor((Date.now() + offset) / 1000),
};
@VeskeR thanks, that helped solve the problem!
Hi! Recently, I have encountered a problem that I often get an error -
Uncaught (in promise) Error: Invalid token; issuedAt time must not be in the future. (See https://help.ably.io/error/40001 for help.)
At the same time, I checked the performance of ably with this error - everything works well, no problems. I decrypted the token I receive from backend, there is an issueAt field inside the tokenOS: Windows 10 Pro 22H2 Version of ably: 2.0.1
┆Issue is synchronized with this Jira Task by Unito