I'm developing a web application with react and typescript. I'm implementing autologin logic, but it's not working. The validity of the JWT token is checked when calling any API, otherwise a 401 error is issued. If the token expires through it, you get the token again through the refresh token. When you receive it again, you receive a refresh token and an access token together. But the problem arises here. In the code below, it was confirmed that the access token was normally received through the refresh token, but when multiple API requests were made in parallel (for example, promise.all request), the token expired in the first request and the access token was requested again. So, it receives normally, but an error occurs because the second request is processed in parallel. The error here is that the access token was retrieved from the first API and expired the second time. how can i solve it? Also, I'd appreciate it if you could let me know how my code went wrong!
import { LoggedInType } from "./../social/socialLoginService";
import { JwtPayload } from "jwt-decode";
import { ReponseType } from "../social/socialLoginService";
import axios, {
AxiosError,
AxiosInstance,
InternalAxiosRequestConfig,
AxiosResponse,
} from "axios";
import storage from "../../utils/storage";
import * as jsonwebtoken from "jsonwebtoken";
const validateTimetoken = (tokenType: "accessToken" | "refreshToken") => {
const accessToken = storage.get(tokenType);
// jwt를 decode 하여 payload를 추출한다.
const decodePayload: JwtPayload = jsonwebtoken.decode(
accessToken as string
) as JwtPayload;
// exp가 UNIX Time으로 나오기 때문에 변환을 해준다.
var exp = new Date((decodePayload.exp as number) * 1000).getTime();
var now = new Date().getTime();
// 변환된 값을 비교하고 Boolean type을 반환한다.
if (now < exp) {
return true;
} else {
return false;
}
};
const request: AxiosInstance = axios.create({
baseURL: process.env.REACT_APP_API,
timeout: 2500,
withCredentials: true,
headers: {
accept: "application/json",
},
});
//Authorization: `Bearer ${window.localStorage.getItem("accessToken")}`,
const onRequest = async (
config: InternalAxiosRequestConfig
): Promise<InternalAxiosRequestConfig> => {
const jwt = storage.get("accessToken", "");
if (!jwt) {
config.headers.accessToken = null;
return config;
}
config.headers.authorization = `Bearer ${jwt}`;
return config;
};
const onErrorResponse = (error: AxiosError | Error): Promise<AxiosError> => {
return Promise.reject(error);
};
request.interceptors.request.use(onRequest, onErrorResponse);
request.interceptors.response.use(
(response: AxiosResponse) => {
console.log(response);
return response.data;
},
async (error) => {
const {
config,
response: { status, data },
} = error;
const refresh = storage.get("refreshToken");
const access = storage.get("accessToken");
if (
validateTimetoken("refreshToken") &&
!validateTimetoken("accessToken")
) {
const {
code,
result: { accessToken, accessTokenAge, refreshToken, refreshTokenAge },
}: ReponseType<LoggedInType> = await axios.post(
`${process.env.REACT_APP_API}/api/v1/auth/token/refresh`,
{},
{
params: { token: refresh },
}
);
storage.set("accessToken", accessToken);
storage.set("accessTokenAge", String(accessTokenAge));
storage.set("refreshToken", refreshToken);
storage.set("refreshTokenAge", String(refreshTokenAge));
request.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;
return axios(config);
}
return Promise.reject(error);
}
);
export default request;