PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Friday, September 2, 2022

[FIXED] How to handle JWT Token Refresh cycle with React and RTK Query: Refetching the failed queries automaticallly

 September 02, 2022     authentication, jwt, reactjs, redux, rtk-query     No comments   

Issue

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.


Solution

On my initial solution, I was able to refetch JWTs if access token is expired but I was unable to refetch the initial failed query automatically for all endpoints. So I had to write try-catch blocks to all endpoints. I didnt want to edit the initial answer not to make the solution look complicated, and its also a valid answer.

Here is the final solution where Redux RTK catches authorization error globally and refreshes the JWTs and after refetches the initial failed query automatically.

Same middleware like in my initial answer. But this time refreshTokens is not a mutation but a query.

// middleware for redux store

const jwtTokenRefresher =
  ({ dispatch }: Record<any, any>) =>
  (next: any) =>
  async (action: any) => {
    if (action && isRejectedWithValue(action)) {
      // Catch the authorization error and refresh the tokens
      console.warn('We got a rejected action!', action.payload.status);
      // console.log({ action });
      if (action.payload.status === 403) {
        const { endpointName, originalArgs } = action.meta.arg;
        // console.log({ type, originalArgs });
        await dispatch(setRejectedAction({ endpointName, originalArgs }));
        await dispatch(backendApi.util.invalidateTags(['Tokens']));
      }
    }

    return next(action);
  };

  1. We retrigger token refresh by .invalidateTagsmethod
  2. We get the meta data of the rejected query from action.meta and save it to the store to regenerate the failed query after we refresh the tokens.

Then we use onQueryStarted method of RTK Query to wait for token refresh to complete:

// query inside createApi

refreshTokens: build.query({
      query: () => ({
        url: API_ROUTE.REFRESH_TOKENS,
        method: 'PATCH',
      }),
      providesTags: ['Tokens'],
      async onQueryStarted(
        arg,
        {
          dispatch,
          getState,,
          queryFulfilled,
          getCacheEntry
        }
      ) {
        await queryFulfilled;

        if (getCacheEntry().isSuccess) {
          const state = <Record<any, any>>getState();
          const {
            app: { rejectedAction = {} },
          } = state;
          const { endpointName, originalArgs } = rejectedAction;
          // console.log({ rejectedAction });
          const queryTrigger = <ThunkAction<any, any, any, any>>(
            backendApi.endpoints[
              endpointName as keyof typeof backendApi.endpoints
            ].initiate(originalArgs)
          );
          await dispatch(queryTrigger);
        }
      },
    }),
  1. await queryFulfilled makes sure the tokens are refreshed.
  2. Get the meta data from the store by const { endpointName, originalArgs } = rejectedAction;
  3. And regenerate the inital query.

Thanks to this flow, Redux RTK Query catches all the failed queries, refreshes JWTs and refetches failed queries automatically.



Answered By - damdafayton
Answer Checked By - Timothy Miller (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing