Перехватчик Axios для обновления токена

Я использую перехватчик Axios, чтобы получить новый токен доступа с токеном обновления. Следующий код создает новый токен доступа с сервера, но я не могу найти файл cookie в браузере.

Работает нормально, но браузер глючит при загрузке.

Было бы очень полезно, если бы кто-нибудь нашел, в чем проблема.

Заранее спасибо.


interface RetryQueueItem {
  resolve: (value?: any) => void;
  reject: (error?: any) => void;
  config: AxiosRequestConfig;
}

const fetchAxiosClient: AxiosInstance = axios.create({
  baseURL: 'http://localhost:8080/api/v1',
  timeout: 60000
});
fetchAxiosClient.defaults.headers.common = {
  'Content-Type': 'application/json',
  'Accept': 'application/json'
} as headers;

export async function createAxiosInstance(requestEvent) {

  // Auth token to request header
  fetchAxiosClient.interceptors.request.use(
    (config: InternalAxiosRequestConfig) => {
      const token = getAccessToken(requestEvent.cookieObj).access_token;
      if (token) {
        config.headers['Authorization'] = `Bearer ${token}`;
      }
      config.headers['Content-Type'] = 'application/json';
      return config;
    },
    error => Promise.reject(error)
  );

  const refreshAndRetryQueue: RetryQueueItem[] = [];
  let isRefreshing = false;

  fetchAxiosClient.interceptors.response.use(
    function (response: AxiosResponse) {
      return response;
    },
    async function (error: any) {
      const originalRequest: AxiosRequestConfig = error.config;
      if (error.response && error.response.status === 401) {
        if (!isRefreshing) {
          isRefreshing = true;
          try {
            const refreshToken = getRefreshToken(requestEvent.cookieObj);
            if (refreshToken) {
              await axios.post(
                `${fetchAxiosClient?.defaults.baseURL}${REFRESH_TOKEN}`,
                {
                  refresh_token: refreshToken,
                }
              ).then(async (response: any) => {
                const cookies = response.headers['set-cookie'] as string[];
                const accessToken = getToken(cookies[0], APP_CONSTANTS.ACCESS_TOKEN);
                setToken(requestEvent.cookieObj, accessToken!);
                return fetchAxiosClient!(originalRequest);
              }).catch((error) => {
                requestEvent.cookieObj.delete(APP_CONSTANTS.ACCESS_TOKEN, { path: '/' });
                throw requestEvent.redirect(302, '/login');
                //return Promise.reject(error);
              });
              refreshAndRetryQueue.forEach(({ config, resolve, reject }) => {
                fetchAxiosClient!(config)
                  .then((response) => resolve(response))
                  .catch((err) => reject(err));
              });
              refreshAndRetryQueue.length = 0;
            } else {
                requestEvent.cookieObj.delete(APP_CONSTANTS.ACCESS_TOKEN, { path: '/' });
              window.location.href = "/";
              return Promise.reject(error);
            }
          } catch (refreshError) {
            refreshAndRetryQueue.length = 0;
            requestEvent.cookieObj.delete(APP_CONSTANTS.ACCESS_TOKEN, { path: '/' });
          } finally {
            isRefreshing = false;
          }
        }
        return new Promise<void>((resolve, reject) => {
          refreshAndRetryQueue.push({ config: originalRequest, resolve, reject });
        });
      }
      return Promise.reject(error);
    }
  );
}

Новый сгенерированный токен доступа, срок действия которого истечет через 15 минут, должен быть установлен как файл cookie. В момент истечения срока действия токена доступа новый токен доступа должен быть сгенерирован с токеном обновления.

Но после истечения срока действия токена доступа при создании нового токена доступа браузер показывает индикатор загрузки и постоянно занят.

🤔 А знаете ли вы, что...
React поддерживает создание контролируемых и неконтролируемых компонентов форм.


112
2

Ответы:

Логика кода выглядит запутанной. Используйте этот плагин, чтобы упростить ваш код:

https://github.com/Flyrell/axios-auth-refresh


Решено

я это упомянул

https://blog.stackademic.com/refresh-access-token-with-axios-interceptors-in-react-js-with-typescript-bd7a2d035562

работает нормально.

export async function createAxiosInstance(cookieObj: Cookie) {
    interface FailedRequests {
      resolve: (value: AxiosResponse) => void;
      reject: (value: AxiosError) => void;
      config: AxiosRequestConfig;
      error: AxiosError;
    }
    // Add the auth token to every request
    fetchAxiosClient.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        const token = getAccessToken(cookieObj).access_token;
        if (token) {
          config.headers['Authorization'] = `Bearer ${token}`;
        }
        config.headers['Content-Type'] = 'application/json';
        return config;
      },
      error => Promise.reject(error)
    );
  
    let failedRequests: FailedRequests[] = [];
    let isTokenRefreshing = false;
  
    fetchAxiosClient.interceptors.response.use(
      function (response: AxiosResponse) {
        return response;
      },
      async (error: AxiosError) => {
        const status = error.response?.status;
        const originalRequestConfig = error.config!;
  
        if (status !== 401) {
          return Promise.reject(error);
        }
  
        if (isTokenRefreshing) {
          return new Promise((resolve, reject) => {
            failedRequests.push({
              resolve,
              reject,
              config: originalRequestConfig,
              error: error,
            });
          });
        }
  
        isTokenRefreshing = true;
        const refreshToken = getRefreshToken(cookieObj);
        try {
          const response = await fetchAxiosClient.post(
            `${fetchAxiosClient?.defaults.baseURL}${REFRESH_TOKEN}`,
                  {
                    refresh_token: refreshToken,
                  }
          );
          const cookies = response.headers['set-cookie'] as string[];
          const accessToken = getToken(cookies[0], APP_CONSTANTS.ACCESS_TOKEN);
          setToken(cookieObj, accessToken!);
          failedRequests.forEach(({ resolve, reject, config }) => {
            fetchAxiosClient(config)
              .then((response) => resolve(response))
              .catch((error) => reject(error));
          });
        } catch (_error: unknown) {
          console.error(_error);
          failedRequests.forEach(({ reject, error }) => reject(error));
          cookieObj.delete(APP_CONSTANTS.ACCESS_TOKEN, { path: '/' });
          return Promise.reject(error);
        } finally {
          failedRequests = [];
          isTokenRefreshing = false;
        }
        return fetchAxiosClient(originalRequestConfig);
      }
    );
  }