Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onRetry and retryInstance not working #107

Closed
architawr opened this issue Aug 5, 2020 · 8 comments
Closed

onRetry and retryInstance not working #107

architawr opened this issue Aug 5, 2020 · 8 comments

Comments

@architawr
Copy link

architawr commented Aug 5, 2020

I have following code:

createAuthRefreshInterceptor(
      axios,
      async () => {
        localStorage.removeItem('accessToken');

        try {
          await store.dispatch('refreshToken', {
            refreshToken: localStorageService.getRefreshToken()
          });

          return Promise.resolve();
        } catch (error) {
            return Promise.reject(error);
        }
      },
      {
        onRetry: config => {
          console.log(config);

          return config;
        }
      }
    );

Refreshing the token works as expected, but when re-requested, the payload is sent in a wrong(string) format (2 image) instead fine(object) format (1 image). I tried to add a handler to the onRetry parameter but nothing happens.
image
image

@Flyrell
Copy link
Owner

Flyrell commented Aug 5, 2020

When you're saving object to local storage, it needs to be "stringified", which I believe your localStorageService is doing behind the scenes. When you get the token from the storage, it needs to be parsed in order to create the object...

// ...
try {
  await store.dispatch('refreshToken', {
    refreshToken: JSON.parse(localStorageService.getRefreshToken()),
  });
} catch () { ... }
// ...

@architawr
Copy link
Author

architawr commented Aug 5, 2020

When you're saving object to local storage, it needs to be "stringified", which I believe your localStorageService is doing behind the scenes. When you get the token from the storage, it needs to be parsed in order to create the object...

// ...
try {
  await store.dispatch('refreshToken', {
    refreshToken: JSON.parse(localStorageService.getRefreshToken()),
  });
} catch () { ... }
// ...

@Flyrell, LocalStorageService in this case is responsible only for accessToken, the problem I described concerns a request that is sent after the function passed as the second parameter. Those, first, a request is sent with a load of 1 image, then a request is sent to receive a fresh token, then a request is sent with a load of 2 images, although it is necessary that the load is from 1 image. That is, a string is placed in the payload of a request that is sent after receiving a fresh token, instead of an object, because of this, the request cannot be processed

@architawr
Copy link
Author

architawr commented Aug 5, 2020

@Flyrell, I was hoping that this problem could be solved by adding an onRetry handler, but the code I indicated above does not work, so I opened this issue. On repeated request, nothing appears in the console, although the onRetry handler is defined

@Flyrell
Copy link
Owner

Flyrell commented Aug 5, 2020

I see. For some reason I thought the screenshots show the token's content 🤦🏻

Also, tests are showing that the onRetry option should work just fine, can you provide the version of axios-auth-refresh you're using, please?

Tests indicate that everything should be working just fine. Would you be able to confirm that no other interceptors are present in your project, that might cause the body to be stringified?

The fastest way we can figure out what's going on would be if you can provide a simple reproduction, as it seems like everything is working properly on my site.

@architawr
Copy link
Author

@Flyrell , yeah of course

"axios": "^0.19.2",
"axios-auth-refresh": "^2.2.8",

Its my api client class

class Client {
  static instance;

  constructor() {
    if (this.instance) return this.instance;

    this.api = axios.create({
      paramsSerializer(params) {
        return qs.stringify(params, {
          skipNulls: true,
          arrayFormat: 'comma',
          serializeDate: d => format(d, "yyyy-MM-dd'T'HH:mm:ss.SSSxxx") // ISO 8601 in local timezone
        });
      }
    });

    createAuthRefreshInterceptor(this.api, async () => {
      localStorage.removeItem('accessToken');

      try {
        await store.dispatch('refreshToken', {
          refreshToken: localStorageService.getRefreshToken()
        });

        return Promise.resolve();
      } catch (error) {
        store.dispatch(
          'api/cancelPendingRequests',
          '[AUTH]: Invalid or expired token'
        );

        store.dispatch('logout').then(() => {
          router.replace({
            name: 'auth.login',
            query: { redirect: router.currentRoute.fullPath }
          });

          return Promise.reject(error);
        });
      }
    },
    {
        onRetry: config => {
          console.log(config);

          return config;
        }
      }
    );

    this.api.interceptors.request.use(
      config => {
        const accessToken = localStorageService.getAccessToken();
        if (accessToken) config.headers.Authorization = accessToken;

        const source = axios.CancelToken.source();
        config.cancelToken = source.token;
        store.commit('api/addCancelTokens', source);

        return config;
      },
      error => Promise.reject(error)
    );

    this.api.interceptors.response.use(
      response => response,
      error => Promise.reject(error)
    );
  }
}

I will describe the situation again for full understanding. A request is sent, which responds with a status of 401, after a request is sent to update the token, the response comes, then the first request is repeated, but in the request body, instead of an object, a string with an object is transmitted (as if JSON.stringify).

Also, with this scenario and the code above, nothing was displayed in the browser console, although, as I expected, when the request is repeated, the onRetry handler will run and the request config will be displayed in the console.

PS: If you need something else to solve this issue, then I am ready to provide everything you need

@Flyrell
Copy link
Owner

Flyrell commented Aug 6, 2020

I can think of two scenarios where this could be failing.

  1. You access a static property with this, you should use ClassName instead, so in the example you provided, this.instance will never exist (also, you're not assigning the instance to the instance property). So, I guess something like this should work for the singleton:
constructor() {
    if (Client.instance) return Client.instance;
    // ...
    Client.instance = this; // <-- last thing in constructor
}
  1. The empty response interceptor at the end might somehow take over the second request, would you please try to remove it and see if it works?

This is all I can think about for right now, I'm pretty sure it's not an issue in the library itself as I've just tried similar scenario myself on a simple project setup and everything works as expected. Also, make sure the v2.2.8 is installed by looking at package-lock.json instead of package.json please.

@architawr
Copy link
Author

The issue has been resolved, thanks. Helped point 2

@Nullpo
Copy link

Nullpo commented Jun 1, 2023

Hi, if someone has this problem in the future, check your axios version :), I had that problem and was solved with yarn add axios@latest.

This was solved in axios v0.21.4 axios/axios#4020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants