correct-apricot
correct-apricot9mo ago

while using POST, PUT, or PATCH if my access token has been expired user will be logged out

I have used axios as my HTTP client and using axios-auth-refresh for refreshing my access token from a server. Everything is working fine except when my access token is expired and if I send any of the above mentioned request then the user will be logged out from the system and redirected to the login page instead of refreshing the access token and retrying the request again. My code for instantiating axiosInstance is as below:
import {
API_BASE_URL, API_KEY, TOKEN_KEY, TOKEN_REFRESH_KEY, USER_DETAILS_KEY
} from "config";

function getAuthToken(token = localStorage.getItem(TOKEN_KEY)) {
return `Bearer ${token}`;
}

export const axiosInstance: AxiosInstance = axios.create();

axiosInstance.interceptors.request.use(
(request: AxiosRequestConfig) => {
if (request.headers) {
request.headers["x-api-key"] = API_KEY;
} else {
request.headers = { "x-api-key": API_KEY };
}
return request;
},
);

const refreshAuthLogic = async (failedRequest: AxiosError) => {
const refreshToken = localStorage.getItem(TOKEN_REFRESH_KEY);
if (refreshToken) {
try {
const { data } = await axios
.post(`${API_BASE_URL}/token/refresh/`, {
refresh: refreshToken,
});
const { access, refresh } = data;
// save token to storage
axiosInstance.defaults.headers.common["Authorization"] = getAuthToken(access);
return Promise.resolve();
} catch {
// remove token from storage
return Promise.reject(failedRequest);
}
}
// remove token from storage
return Promise.reject(failedRequest);
};

createAuthRefreshInterceptor(
axiosInstance,
refreshAuthLogic,
{
pauseInstanceWhileRefreshing: true,
}
);
import {
API_BASE_URL, API_KEY, TOKEN_KEY, TOKEN_REFRESH_KEY, USER_DETAILS_KEY
} from "config";

function getAuthToken(token = localStorage.getItem(TOKEN_KEY)) {
return `Bearer ${token}`;
}

export const axiosInstance: AxiosInstance = axios.create();

axiosInstance.interceptors.request.use(
(request: AxiosRequestConfig) => {
if (request.headers) {
request.headers["x-api-key"] = API_KEY;
} else {
request.headers = { "x-api-key": API_KEY };
}
return request;
},
);

const refreshAuthLogic = async (failedRequest: AxiosError) => {
const refreshToken = localStorage.getItem(TOKEN_REFRESH_KEY);
if (refreshToken) {
try {
const { data } = await axios
.post(`${API_BASE_URL}/token/refresh/`, {
refresh: refreshToken,
});
const { access, refresh } = data;
// save token to storage
axiosInstance.defaults.headers.common["Authorization"] = getAuthToken(access);
return Promise.resolve();
} catch {
// remove token from storage
return Promise.reject(failedRequest);
}
}
// remove token from storage
return Promise.reject(failedRequest);
};

createAuthRefreshInterceptor(
axiosInstance,
refreshAuthLogic,
{
pauseInstanceWhileRefreshing: true,
}
);
and in my App.ts I am using this axiosInstance in Refine config as
<Refine
dataProvider={restDataProvider(API_BASE_URL, axiosInstance)}
// other configs
/>
<Refine
dataProvider={restDataProvider(API_BASE_URL, axiosInstance)}
// other configs
/>
12 Replies
kapa.ai
kapa.ai9mo ago
Hi @dipbazz. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in the extracts, it seems like you have correctly set up the axios-auth-refresh interceptor for refreshing the access token. However, the issue you're facing might be due to the error handling in your authProvider. In refine, the onError method of the authProvider is called when an error response is received from the API. If this method returns a promise resolving to an object with logout: true, the user will be logged out. This might be the reason why your users are being logged out when the access token expires. Here's an example of how the onError method can be set up to log out the user if the API returns a 401 or 403 error:
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 401 || error.status === 403) {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 401 || error.status === 403) {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
(Source: refine.dev/docs/api-reference/core/providers/auth-provider/ (https://refine.dev/docs/api-reference/core/providers/auth-provider/)) If your onError method is set up in a similar way, it might be logging out the user when a 401 error (which usually indicates an expired token) is received. You might want to adjust this logic to not log out the user immediately when a 401 error is received, and instead let the axios-auth-refresh interceptor handle the token refresh. Please note that this is just a suggestion based on the information provided in the extracts. You might need to adjust this based on your specific application's requirements and behavior. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@494653850632978472> kapa.ai is still learning and improving, please let us know how it did by reacting below
correct-apricot
correct-apricot9mo ago
@kapa.ai how can I setup my onError method to logout only when the refresh token is expired not the access token?
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To set up your onError method to only log out when the refresh token is expired, you need to handle the specific error response that your server sends when the refresh token is expired. This will depend on how your server is set up. For example, if your server sends a 401 status code when the access token is expired and a 403 status code when the refresh token is expired, you can set up your onError method like this:
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 403) {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 403) {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
(Source: refine.dev/docs/api-reference/core/providers/auth-provider/ (https://refine.dev/docs/api-reference/core/providers/auth-provider/)) In this example, the user will only be logged out if the server returns a 403 status code, which we are assuming means that the refresh token is expired. If the server returns a 401 status code (access token expired), the onError method will not log out the user, and the axios-auth-refresh interceptor should handle the token refresh. Please adjust the status codes and logic according to your server's behavior. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@494653850632978472> kapa.ai is still learning and improving, please let us know how it did by reacting below
correct-apricot
correct-apricot9mo ago
@kapa.ai the server sends 401 for both access and refresh token is expired.
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- If your server sends a 401 status code for both expired access and refresh tokens, you'll need to differentiate these two scenarios based on the error message or some other property in the error response. Assuming your server sends a specific error message when the refresh token is expired, you can set up your onError method like this:
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 401 && error.message === "Refresh token expired") {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
const authProvider: AuthBindings = {
// ---
onError: async (error) => {
if (error.status === 401 && error.message === "Refresh token expired") {
return {
logout: true,
redirectTo: "/login",
error,
};
}
return {};
},
// ---
};
(Source: refine.dev/docs/api-reference/core/providers/auth-provider/ (https://refine.dev/docs/api-reference/core/providers/auth-provider/)) In this example, the user will only be logged out if the server returns a 401 status code and the error message is "Refresh token expired". If the server returns a 401 status code with a different error message (which we are assuming means that the access token is expired), the onError method will not log out the user, and the axios-auth-refresh interceptor should handle the token refresh. Please adjust the error message and logic according to your server's behavior. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@494653850632978472> kapa.ai is still learning and improving, please let us know how it did by reacting below
exotic-emerald
exotic-emerald9mo ago
Hello @dipbazz, was the latest answer useful for you? To add on that, I think you can reject with a specific error message if you fail to refresh token and catch only that error on onError method. It should do the trick.
correct-apricot
correct-apricot9mo ago
Hey @batuhanw, I am looking on it, needs some changes on the server side. Will let you know if that fixes my issue. Thank you for your response. Hey @batuhanw @kapa.ai, I have managed to solve it using a check for a access token in a local storage. Because, if there is not access token in the storage then the refresh auth logic had some problem, so user gets logged out. This is working fine for me. But now I have a problem that refresh API gets hit and I get back access and refresh token but the axios won't retry for that failed request. Is this problem related to axios-auth-refresh or axios?
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The issue you're facing seems to be related to the axios-auth-refresh package. When the access token is refreshed, the failed request should be retried automatically. If this is not happening, it might be due to how the axios-auth-refresh interceptor is set up. In your refreshAuthLogic function, after refreshing the token, you should be updating the Authorization header of the failed request before resolving the promise:
const refreshAuthLogic = (failedRequest) =>
axiosInstance
.post(`${API_URL}/auth/token/refresh`)
.then((tokenRefreshResponse) => {
localStorage.setItem("token", tokenRefreshResponse.data.token);

failedRequest.response.config.headers["Authorization"] =
"Bearer " + tokenRefreshResponse.data.token;

return Promise.resolve();
});
const refreshAuthLogic = (failedRequest) =>
axiosInstance
.post(`${API_URL}/auth/token/refresh`)
.then((tokenRefreshResponse) => {
localStorage.setItem("token", tokenRefreshResponse.data.token);

failedRequest.response.config.headers["Authorization"] =
"Bearer " + tokenRefreshResponse.data.token;

return Promise.resolve();
});
(Source: refine.dev/docs/tutorial/understanding-authprovider/create-authprovider/ (https://refine.dev/docs/tutorial/understanding-authprovider/create-authprovider/)) If you're doing this correctly and the failed request is still not being retried, it might be a problem with the axios-auth-refresh package itself, or there might be some other issue with your code. You might want to check the axios-auth-refresh repository (https://github.com/Flyrell/axios-auth-refresh) for more information or to report an issue. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@494653850632978472> kapa.ai is still learning and improving, please let us know how it did by reacting below