Home > Net >  How to handle JWT Token Refresh cycle with React and RTK Query
How to handle JWT Token Refresh cycle with React and RTK Query

Time:08-21

In my application I have a token refresh endpoint /refresh which sets new JWT refresh token in a cookie and sends back new JWT access token as json.

Since access token expires after 5 minutes, I need to implement an under the hood refresh logic to kick in whenever access token is expired.

Currently what I am doing is to call the api through RTK Query and if api rejects the query, I call the refreshTokens mutation.

And I need to put this logic in all api queries like this:

 updateProfile(body)
      .unwrap()
      .catch((error) => {
        if (error && error.status === 403) {
          refreshTokens(null); // RTK Query Mutation
          updateProfile(body); // RTK Query Mutation
        }
      });

Doing this seems repetition of code since it needs to be implemented to all the calls to the api.

I wonder if there is a global solution to automatically call the refresh token endpoint when the queries are rejected.

CodePudding user response:

Hi I have done something similar to what you have done ,, and without the need to repeat the code to each API call,,

I have created my own fetch function which will receive two parameters, one is a function that call the fetch function and the other one is a function that calls the refresh token.

const fetchOrRefresh = async (fetchFun, refreshFun) => {
  // first fetch call
  let fetchResponse = await fetchFun();
  let refreshResponse;

  // check if the call was ok, in my case api is returning and object that 
  // contains error object
  if (Object.keys(fetchResponse).includes("error")) {
    refreshResponse = await refreshFun();
  }

  // check if the refresh call was successful 
  if (refreshResponse && Object.keys(refreshResponse).includes("error")) {
  // if not ,, return anything or thow an error
    return null;
  }

  // recall the fetch and return what it returns
  return fetchFun();
};

maybe you can refactor it with the use of the returned information from the error message to build it in a better way to build it as a middleware …

CodePudding user response:

This is how i solved it so far:

  1. Created a middleware to catch authorization errors on macro level based on this doc. It catches the authorization errors and calls the mutation which refreshes JWTs, without using React hooks which is explained in this doc. Also its important to attach error handling middleware before RTK Query middleware.
const jwtTokenRefresher =
  ({ dispatch }: Record<any, any>) =>
  (next: any) =>
  async (action: any) => {
    if (isRejectedWithValue(action)) {
      // Catch the authorization error and refresh the tokens
      if (action.payload.status === 403) {
        console.warn('We got a rejected action!');
        await dispatch(
          api.endpoints.refreshTokens.initiate(null)
        );
      }
    }

    return next(action);
  };

export const store = configureStore({
  reducer: {
//...
    [api.reducerPath]: api.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware()
      .concat(jwtTokenRefresher)
      .concat(api.middleware),
});
      
  1. After we get new tokens by middleware, we are calling the initial mutation within its try/catch block again 'without react hooks'.
update: build.mutation({
      query: (body) => ({
        url: //,
        method: 'POST',
        body,
      }),
      async onQueryStarted(
        arg,
        { dispatch, getState, queryFulfilled, requestId, extra, getCacheEntry }
      ) {
        try {
     //
        } catch (error: any) {
          if (error.error.status === 403) {
            await dispatch(api.endpoints.update.initiate(arg));
          }
        }
      },
    }),

So far it works as I wanted it to be. My only concern is the need to catch error on all the mutations and calling the mutation again. I would be interested if anyone has any idea to handle it globally as well.

  • Related