jwtfetchreact-queryky

access token doesn't change on ky retry request


I am trying to pass my access token to requests made with ky and retry them on 401 error with an updated token. On retry the token updates but the requests themselves still have the old token. How do I update token correctly?

export const useKy = () => {
  const { auth, setAuth, refreshToken } = useAuth();

  const customKy = ky.extend({
    prefixUrl: 'api/v1',
    hooks: {
      beforeRequest: [
        async (request) => {
          if (auth) {
            request.headers.set('Authorization', `Bearer ${auth}`);
            request.headers.set('Content-Type', 'application/json');
          }
        },
      ],
      beforeRetry: [
        async ({ request, error, retryCount }) => {
          if (error instanceof HTTPError && error.response.status === 403 && retryCount === 1) {
            try {
              const newAccessToken = await refreshToken().then((newAccessToken) => {
                setAuth(newAccessToken.access_token);
                return newAccessToken;
              });
              console.log(newAccessToken.access_token);

              request.headers.set('Authorization', `Bearer ${123}`);
            } catch (error) {
              throw new Error('Failed to refresh token');
            }
          }
        },
      ],
    },
    retry: {
      methods: ['get', 'post'],
      limit: 3,
      statusCodes: [403],
    },
  });

  return { customKy };
};

An example of a request:

export const useInfiniteLogsQuery = (offset: number = 0, pageLimit: number = 25) => {
  const { customKy } = useKy();
  return useInfiniteQuery({
    queryKey: ['logs1'],
    queryFn: async () => {
      try {
        let link = `logs-list/json/?offset=${offset}&limit=${pageLimit}`;
        const request = await customKy.get<FetchLogsResponse>(link).json();

        return request;
      } catch (error) {
        console.error('Failed to fetch logs:', error);
        throw error;
      }
    },
    initialPageParam: undefined,
    getNextPageParam: (lastPage) => (lastPage.next ? offset : null),
  });
};

Solution

  • I've rewritten it a bit and it seems to be working now. But I still don't understand what was wrong. I've used afterResponse instead of beforeRetry:

    export const useKy = () => {
      const { auth, setAuth, refreshToken } = useAuth();
    
      const customKy = ky.extend({
        prefixUrl: 'api/v1',
        hooks: {
          beforeRequest: [
            async (request) => {
              console.log('request');
    
              if (auth) {
                request.headers.set('Authorization', `Bearer ${auth}`);
                request.headers.set('Content-Type', 'application/json');
              }
            },
          ],
            afterResponse: [
            async (request, _, response) => {
              if (response.status === 403) {
                try {
                  await refreshToken().then((newAccessToken) => {
                    setAuth(newAccessToken.access_token);
                    console.log('retry', newAccessToken.access_token);
                    request.headers.set('Authorization', `Bearer ${newAccessToken.access_token}`);
                  });
    
                  return ky(request);
                } catch (error) {
                  throw new Error('Failed to refresh token');
                }
              }
            },
          ],
        },
        retry: {
          methods: ['get', 'post'],
          limit: 3,
          statusCodes: [403],
        },
      });
    
      return { customKy };
    };