How to disable auto token refreshment for RingCentral JavaScript SDK

Give you all the control over token management

Tyler Liu
RingCentral Developers
4 min readApr 22, 2024

--

RingCentral JavaScript SDK is available here: https://www.npmjs.com/package/@ringcentral/sdk

It’s source code is here: https://github.com/ringcentral/ringcentral-js/tree/master/sdk

The SDK

A sample application is like this:

import { SDK } from '@ringcentral/sdk';

const sdk = new SDK({
server: process.env.RINGCENTRAL_SERVER_URL,
clientId: process.env.RINGCENTRAL_CLIENT_ID,
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
});

// we print all the HTTP requests
const client = sdk.client();
client.on(client.events.requestSuccess, (apiResponse) => {
console.log('We made an API call to', apiResponse.url);
});

const platform = sdk.platform();

const main = async () => {
await platform.login({
jwt: process.env.RINGCENTRAL_JWT_TOKEN,
});
await platform.get('/restapi/v1.0/account/~/extension/~');
};
main();

Sample output:

We made an API call to https://platform.ringcentral.com/restapi/oauth/token
We made an API call to https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~

The first line of output is generated by this API call:

await platform.login({
jwt: process.env.RINGCENTRAL_JWT_TOKEN,
});

The second line of output is generated by this API call:

await platform.get('/restapi/v1.0/account/~/extension/~');

The interesting fact

RingCentral access token expires in 1 hour. What will happen if we invoke an API after 2 hours? You may expect an exception. Because you cannot invoke RingCentral API with an expired access token. Will there be an exception?

Let’s try:

import { SDK } from '@ringcentral/sdk';
import waitFor from 'wait-for-async';

...
await pltaform.login({
jwt: process.env.RINGCENTRAL_JWT_TOKEN
});
await platform.get('/restapi/v1.0/account/~/extension/~');
await waitFor({ interval: 7200000 }); // 2 hours
await platform.get('/restapi/v1.0/account/~/extension/~');

The output is:

We made an API call to https://platform.ringcentral.com/restapi/oauth/token
We made an API call to https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~
We made an API call to https://platform.ringcentral.com/restapi/oauth/token
We made an API call to https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~

Surprisingly, there is no exception! The first two lines of output are same as before. There are two more lines of output. The last line of output is for our API call, there is no exception. What is the 3rd line? It says so:

We made an API call to https://platform.ringcentral.com/restapi/oauth/token

It turns out that the SDK automatically refreshed the access token for us! It’s magic! Let’s dive into the source code of the SDK and see how it work:

When we invoke the API, we use the platform.get(...) method: https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L787-L789

which invokes the platform.send(...) method: https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L761

which invokes the platform.sendRequest(…) method: https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L716

which invokes the platform.inflateRequest(…) method: https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L698

which invokes the platform.ensureLoggedIn(…) method: https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L807

which invokes the platform.refresh(…) method:https://github.com/ringcentral/ringcentral-js/blob/77484be7fc946e72f1b119fd31e8a49d72e31497/sdk/src/platform/Platform.ts#L810

That’s such a long method call chain! Let me summarize what it does: when you invoke an API, the SDK will check your access token expire time, if it expires, the SDK will automatically refresh the token for you.

This interesting fact is both a blessing and a curse. For people scratching quick and dirty simple applications, it saves their headache of refreshing the token themselves. But for complicated enterprise applications, this token auto refreshing feature could be really annoying.

The issue/problem

More often than not, developers want to manage token lifecycle themselves, especially for complicated enterprise applications. For example, some developer may store the token in a central database, and share the token among multiple applications (or multiple instances of the same application code base).

If multiple applications sharing the same token are all use the JavaScript SDK, by default, the SDK will automatically refresh the token, which will cause several issues:

  • token synchronization: one application refreshes the token will cause all other applications to have a revoked token. Thus, it is necessary to sync the token to all other applications.
  • racing condition: if two/multiple applications try to refresh the token at the same time, RingCentral server will make sure that only one of them will succeed. You may encounter token refresh failure due to this reason.
  • hard to take full control of the token lifecycle: let’s say you write a dedicated application to maintain the token’s lifecycle. This specially coded app will make sure that tokens are properly refreshed and maintained. If all the token consuming applications also refresh the token, it will again bring up the token synchronize and racing condition problem.

Can we just get rid of this problem?

The solution

First of all, I would like to emphasize that this solution is not necessary for simple applications. Let’s say you have an simple desktop application that you start once a day to send a fax. If it works, you don’t need to change it. You don’t need to care about how the token is refreshed.

If your application is distributed, tokens are shared among different servers/clients, and you want to centrally manage the token’s lifecycle. In the meantime, you don’t want anything magical happen to your token. You want full control over token’s lifecycle. If it sounds like your case, then the solution is for you.

The solution is very simple:

platform.ensureLoggedIn = async () => null;

Just rewrite the platform.ensureLoggedIn method and make sure it does nothing. This is because, we want to manage the token ourselves, we don’t need the ensureLoggedIn feature provided by the SDK. Please note that, if you are using the platform.ensureLoggedIn method directly in your code base, you need to change it to some other code. Since this method has been overridden to do nothing.

Then how do we refresh the token? easy:

await platform.refresh();

Or you may setup a timer to do it every 30 minutes:

setInterval(async () => {
await platform.refresh();
}, 1800000);

Or you may create a dedicated application to refresh the token and let the app trigger by cron jobs.

You have the full flexibility to decide when and how you refresh your token.

Please note that, if multiple apps are using the token, you still need to sync the new token to all apps after token refreshment. This is inevitable. But at least, you token is bing refreshed and managed in a central place. There is no more magic happening to your tokens.

Source code

The source code for this article is available here: https://github.com/tylerlong/rc-js-sdk-no-auto-refresh-token-demo

--

--