2

I was trying to do it like this, but for three requests it sends three refresh requests:

  • 1, 2, 3 fails with 401
  • refresh success, 1 success, 2, 3 fails
  • refresh success, 2 success, 3 fails
  • refresh success 3 success

I can't put that much load on a mobile device (even if there was only 3 refresh without "refail")

Here is my code:

function requestRefreshToken(refreshToken, accessToken) {
  return axios
    .create({
      baseURL: apiUrl + Endpoints.AUTH.REFRESH,
      skipAuthRefresh: true,
      headers: {
        'Accept-Language': 'ru',
        'User-Agent': `${Platform.OS} ${packageJson.version}`,
        Authorization: accessToken,
      },
    })
    .post(
      '',
      {
        grant_type: GrantTypes.REFRESH_TOKEN,
        refresh_token: refreshToken,
        client_id: Client.id,
        client_secret: Client.secret,
      },
      { validateStatus },
    );
}

const refreshAuthLogic = async failedRequest =>
  Keychain.getCredentials()
    .then(old => requestRefreshToken(old.refreshToken, old.accessToken))
    .then(({ data: credentials }) => {

      failedRequest.config.headers.Authorization = `${credentials.token_type} ${
        credentials.access_token
      }`;

      return Keychain.setCredentials(credentials);
    });

createAuthRefreshInterceptor(axios, refreshAuthLogic, {
  retryInstance: axios,
  skipWhileRefreshing: true,
  onRetry: function(config) {
    return Keychain.getCredentials().then(({ accessToken }) =>
      axios({
        ...config,
        header: { ...config.headers, Authorization: accessToken },
      }),
    );
  },
});

axios.interceptors.response.use(
  r => r,
  request => {
    if (request.response.status === 401) {
      return Keychain.getCredentials().then(({ accessToken }) =>
        axios({
          ...request.config,
          header: { ...request.config.headers, Authorization: accessToken },
        }),
      );
    }
  },
);
  • are you sure the additional requests are not triggered by the second interceptor you have at the end of the code? This should not be happening when using skipWhileRefreshing set to true – Dawid Zbiński Feb 23 '20 at 17:22
  • Also, access token should also be added to the requests by an interceptor. That way whenever the request is retried, the interceptor is binds the correct token. You would, of course, need to store the token in a state or variable, which should not be a problem. – Dawid Zbiński Feb 23 '20 at 17:26
  • If I remove additional interceptor and set skipWhileRefreshing to false we'll get the next: 1) all 3 request fails with 401 2) refresh success 3) either 1st success, two others failed either 1st success two others getting looped with failure – Alexandr Dobrovolsky Feb 25 '20 at 08:12

2 Answers2

1

Solved with this! Seems there is no way solving it without failed request queue( https://gist.github.com/mkjiau/650013a99c341c9f23ca00ccb213db1c

0

Here's a quick way I implemented it. I found it a bit simpler to reason about the code. I've adapted it from this solution: HERE

let refreshTokenPromise: null | Promise < any > ;

instance.interceptors.response.use(r => {
  const {
    data
  } = r;
  if (data.errors && data.errors[0].message === "AUTH_EXPIRED") {
    if (!refreshTokenPromise) {
      refreshTokenPromise = fetchRefreshToken().then(data => {
        refreshTokenPromise = null;
        return data;
      });
    }

    return refreshTokenPromise.then(token => {
      if (r.config.headers) r.config.headers["Authorization"] = token;
      return instance.request(r.config);
    });
  }
  return r;
});
Rob
  • 164
  • 1
  • 4