2

I use tokens as authorization, when the token expires, I want to create a new token using the refresh token. I can do this with the axios library but I couldn't find how to do it with umi request. Anyone translate the code sample given below to an umi request interceptor example?

this axios interceptors response for my refresh token operation.

  let isRefreshing: boolean = false;
  let failedQueue: any = [];

  const processQueue = (error: any, token: any = null) => {
    failedQueue.forEach((prom: any) => {
      if (error) {
        prom.reject(error);
      } else {
        prom.resolve(token);
      }
    });

    failedQueue = [];
  };

axios.interceptors.response.use(
    async response => {
      return response;
    },
    async error => {
      debugger;
      const originalRequest = error.config;
      if (error.response) {
        const status = error.response.status;
        const refreshToken = getStoredUserAuth().refreshToken;
        if (status === 401 && !originalRequest._retry) {
          if (isRefreshing) {
            return new Promise(function(resolve, reject) {
              failedQueue.push({ resolve, reject });
            })
              .then(token => {
                originalRequest.headers["Authorization"] = "Bearer " + token;
                return axios(originalRequest);
              })
              .catch(err => {
                return Promise.reject(err);
              });
          }

          originalRequest._retry = true;
          isRefreshing = true;

          return new Promise(function(resolve, reject) {
            refreshAccessToken(refreshToken)
              .then(response => {
                if (response.status === 200) {
                  if (response.data.token) {
                    setAuthStatus(response.data);
                    axios.defaults.headers.common["Authorization"] =
                      "Bearer " + response.data.token;
                    originalRequest.headers["Authorization"] =
                      "Bearer " + response.data.token;
                  }
                  processQueue(null, response.data.token);
                  resolve(axios(originalRequest));
                } else {
                  throw new Error();
                }
              })
              .catch(err => {
                //console.log(err);
                failedQueue = [];
                setUnauthStatus();
                source.cancel("Session time out");
                processQueue(err, null);
                reject(err);
              })
              .then(() => {
                isRefreshing = false;
              });
          });
        }
      } else {
        debugger;
        errorHandler(error);
      }
      return Promise.reject(error);
    }
  );

I use to ant design pro V5 version, so I've tried to refresh token with umi request but I could'nt do it, As in the axios library, I cannot write for umi request, I cannot establish its logic. So can anyone write the same example (axios.interceptors.response) above for umi request?

const requestInterceptors = (url: string, options: RequestOptionsInit) => {
  if (tokenService.check()) {
    const headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/x-www-form-urlencoded',
      'Authorization': `Bearer ${tokenService.parse()?.token}`,
    };
    return {
      url,
      options: { ...options, headers },
    };
  }
  return {
    url,
    options: { ...options },
  };
};

const responseInterceptors = (response: Response, options: RequestOptionsInit) => {

// I could'nt this part for refresh token like axios interceptors. I need help for that

return response;
}

export const request: RequestConfig = {
  errorHandler,
  requestInterceptors: [requestInterceptors],
  responseInterceptors: [responseInterceptors]
};

umi request github link for documentation

CodAvo
  • 183
  • 1
  • 2
  • 16

1 Answers1

3

Here's my solution for handling refresh token in umi-request/ant-design-pro, no need for queues, works like a charm

// Neccesary imports
import type { RequestConfig } from 'umi';
import { request as requestUmi } from 'umi';
import type {
  RequestInterceptor,
  RequestOptionsInit,
  ResponseError,
} from 'umi-request';
import Reqs from 'umi-request';
import merge from 'lodash/merge';
import cloneDeep from 'lodash/cloneDeep';

// My personal type definitions
namespace API {
  export interface LoginResult {
    access: string;
    refresh: string;
  }
}

// Part 1: "Simple" Error Handler =))
const errorHandler = (error: ResponseError) => {
  console.log('HTTP ERROR', error);
  throw error;
};

// Part 2: Request Interceptors, (use this instead of "headers" directly in request config)
const requestInterceptor: RequestInterceptor = (url, options) => {
  return {
    url,
    options: merge(cloneDeep(options), {
      headers: { Authorization: `Bearer ${jwt.getAccess()}` },
    }),
  };
};

// Part 3: Response Interceptos
const { cancel } = Reqs.CancelToken.source();
let refreshTokenRequest: Promise<API.LoginResult> | null = null;
const responseInterceptor = async (
  response: Response,
  options: RequestOptionsInit
) => {
  const accessTokenExpired = response.status === 401;
  if (accessTokenExpired) {
    try {
      if (!refreshTokenRequest) {
        refreshTokenRequest = refreshAccessToken(jwt.getRefresh());
      }
      // multiple requests but "await"ing for only 1 refreshTokenRequest, because of closure
      const res = await refreshTokenRequest;
      if (!res) throw new Error();
      if (res.access) jwt.saveAccess(res.access);
      if (res.refresh) jwt.saveRefresh(res.refresh); // for ROTATE REFRESH TOKENS
      return requestUmi(
        response.url,
        merge(cloneDeep(options), {
          headers: { Authorization: `Bearer ${res.access}` },
        })
      );
    } catch (err) {
      jwt.removeAccess();
      jwt.removeRefresh();
      cancel('Session time out.');
      throw err;
    } finally {
      refreshTokenRequest = null;
    }
  }

  return response;
};

export const request: RequestConfig = {
  errorHandler,
  // This would fuck the refresh token logic, use requestInterceptors instead,
  // because jwt.getAccess() will not being called everytime, but only the first time => lead to stale access token
  // headers: { Authorization: `Bearer ${jwt.getAccess()}` },

  // Handle refresh token (old): https://github.com/ant-design/ant-design-pro/issues/7159#issuecomment-680789397
  // Handle refresh token (new): https://gist.github.com/paulnguyen-mn/8a5996df9b082c69f41bc9c5a8653533
  requestInterceptors: [requestInterceptor],
  responseInterceptors: [responseInterceptor],
};

Also posted here

Loi Nguyen Huynh
  • 8,492
  • 2
  • 29
  • 52