Refresh token silently in alovajs
Tags: react, svelte, vue, alova
Published: 2023-12-18 06:14:59

alova is a lightweight request strategy library designed to simplify the management and use of interfaces. By simply configuring parameters, you can implement complex requests such as sharing requests, paging requests, form submissions, breakpoint resumption, etc., without writing a lot of code, improving development efficiency, application performance, and reducing server pressure.

When requesting, we often need to authorize login through token, and refresh the token through refresh_token when the token expires. Here we will discuss how to refresh the token without any sense through alova, that is, automatically refresh the token when the token is found to have expired. At the request initiation point It will not feel that the token has been refreshed.

> At present, the token authentication module has been launched. It only requires simple configuration to achieve senseless token refresh. It is recommended to use it directly. [View details->](https://alova.js.org/tutorial/strategy/tokenAuthentication)

The following are two ideas for implementing a senseless refresh token. Each idea implements the function of the token to wait for other requests during the refresh period.

## Determine token expiration on the front end

This is used to determine whether the token has expired at the front end. This is usually used when the token expiration time is returned during login. The idea is to determine whether the token has expired in `beforeRequest`. If it has expired, refresh the token first and then continue to send the request. Its advantage is A response with an expired token will not be received, so there is no need to send repeated requests.

```js
let token = '', refreshToken = '', tokenRefreshing = false, waitingList = [];
const alovaInstance = createAlova({
   // ...
   beforeRequest: async method => {
     if (getCache('token_expire_time') < Date.now() && method.meta?.authType !== 'refreshToken') { // token expires
       if (tokenRefreshing) {
         // If the token is being refreshed, wait for the refresh to complete before sending a request.
         console.log('Refreshing token, waiting for request');
         await new Promise(resolve => {
           waitingList.push(resolve);
         });
       }

       try {
         tokenRefreshing = true;
         const { refreshToken: newRefreshToken, token: newToken } = await tokenRefresh({ refreshToken });
         //Save the new token and refreshToken
         token = newToken;
         refreshToken = newRefreshToken;

         tokenRefreshing = false;
         // After refreshing the token, notify the requests in the waiting list
         waitingList.forEach(resolve => resolve());
         waitingList = [];
       } catch(error) {
         // If the refresh token fails, you can handle operations such as jumping to the login interface.
         // ...
       }
     }

     method.config.headers.Authorization = token;
   },
})
```

Here the tokenRefresh request method adds `meta data of `authType = 'refreshToken' as an identifier before the request can pass.

```js
export const tokenRefresh = () => {
   const method = alovaInst.Get('/refresh-token', {
     localCache: null
   });
   method.meta = {
     authType: 'refreshToken'
   };
   return method;
}
```

## Determine token expiration based on the status returned by the server.

This method is to determine whether the token has expired through the return status of the server. This is usually used when the token expiration time is not returned during login. The idea is to determine whether the token has expired by returning the status in `responded.onSuccess`. If it has expired, refresh the token. The advantage of sending the request again is that it is more accurate, but duplicate requests will also be sent.

```js
let token = '', refreshToken = '';
let tokenRefreshing = false; // Refreshing token, let other requests wait first
let waitingList = [];
const alovaInst = createAlova({
   // ...
   beforeRequest: async ({ url, config, meta }) => {
     console.log(url, 'Requesting');
     // If the token is being refreshed, wait for the refresh to complete before sending a request.
     if (tokenRefreshing && meta?.authType !== 'refreshToken') {
       console.log('Refreshing token, waiting for request');
       await new Promise(resolve => {
         waitingList.push(resolve);
       });
     }
     config.headers.Authorization = token;
   },
   responded: async (response, method) => {
     if (response.status === 401) { // token expires
       console.log(method.url, 'Token expires, get token again');
       try {
           // This prevents requests to refresh the token multiple times, and intercepts and waits for requests sent before the token refresh is completed.
           if (tokenRefreshing) {
             console.log('Refreshing token, waiting for request');
             await new Promise(resolve => {
               waitingList.push(resolve);
             });
           })
         tokenRefreshing = true;
         const { token: newToken, refreshToken: newRefreshToken } = await refreshToken({ refreshToken });
         token = newToken;
         refreshToken = newRefreshToken;

         tokenRefreshing = false;
         waitingList.forEach(resolve => resolve());
         waitingList = [];
         console.log('New token has been obtained and saved', token, `Revisit ${method.url}`);

         // This is because the original interface is re-requested, and superposition with the previous request will result in repeated calls to transformData, so transformData needs to be left empty to remove one call.
         const methodTransformData = method.config.transformData;
         method.config.transformData = undefined;
         const dataResent = await method;
         method.config.transformData = methodTransformData;
         return dataResent;
       } catch(error) {
         // If the refresh token fails, you can handle operations such as jumping to the login interface.
         // ...
       }
     }
     return response.json();
   },
});
```

So far, Alova's two ideas for refreshing tokens have been implemented, and there is no sense of token refresh at the request initiation point.

If you want to learn more about the usage of alovajs, welcome to [alova official website](https://alova.js.org/) to learn. If you also like alovajs, please contribute a star in [Github repository](https://github.com/alovajs/alova), which is very important to us.

If you think the article is helpful to you, please don't be stingy with your likes and comments. Tell me what you think of alovajs, or ask some questions. I will try my best to answer them. Your support is the biggest motivation for my creation! Hahahahahaha~

### Welcome to join our community

If you have any questions, you can join the following group chat for consultation, or you can publish Discussions in [github repository](https://github.com/alovajs/alova/discussions). If you encounter problems, please also post in [github issues ](https://github.com/alovajs/alova/issues) and we will solve it as soon as possible.

- [Join the Discord community to participate in communication](https://discord.gg/S47QGJgkVb)

You are also welcome to contribute, please go to [Contribution Guide](https://alova.js.org/contributing/overview).