How to implement jwt authentication in authProvider?

I have followed this link https://refine.dev/docs/api-reference/core/providers/auth-provider/#setting-authorization-credentials to set my authorization credentials and it is working fine. But now how do I use a refresh token to generate a new access token on every request. What I have done so far is below:
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const prevRequest = error?.config;
if (error?.response?.status === 401 && !prevRequest?.sent) {
prevRequest.sent = true;
axiosInstance.post(
`${API_BASE_URL}/token/refresh/`,
{ refresh: localStorage.getItem(TOKEN_REFRESH_KEY) }
).then((res => {
prevRequest.headers = { ...prevRequest.headers, ...authHeader(res.data.access) };
return axiosInstance(prevRequest);
}))
.catch((err) => {
if(err.response.status === 401) {
sessionStorage.removeItem(TOKEN_KEY);
return Promise.reject(new Error('Your login session expired. Please login again.'));
}
return Promise.reject(err);
});
}
const customError: HttpError = {
...error,
message: error.response?.data?.message,
statusCode: error.response?.status,
};
return Promise.reject(customError);
},
);
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const prevRequest = error?.config;
if (error?.response?.status === 401 && !prevRequest?.sent) {
prevRequest.sent = true;
axiosInstance.post(
`${API_BASE_URL}/token/refresh/`,
{ refresh: localStorage.getItem(TOKEN_REFRESH_KEY) }
).then((res => {
prevRequest.headers = { ...prevRequest.headers, ...authHeader(res.data.access) };
return axiosInstance(prevRequest);
}))
.catch((err) => {
if(err.response.status === 401) {
sessionStorage.removeItem(TOKEN_KEY);
return Promise.reject(new Error('Your login session expired. Please login again.'));
}
return Promise.reject(err);
});
}
const customError: HttpError = {
...error,
message: error.response?.data?.message,
statusCode: error.response?.status,
};
return Promise.reject(customError);
},
);
After using this code I can get new access token there isn't any problem. But once the refresh token is expired then how can I logout user. By using above code the request for refresh token is request infinitely.
Auth Provider | refine
refine let's you set authentication logic by providing the authProvider property to the `` component.
Omer
Omer577d ago
Hey @dipbazz , @yildirayunlu can help us 🎯
ratty-blush
ratty-blush577d ago
Okay @Omer.
ambitious-aqua
ambitious-aqua577d ago
Hi @dipbazz I did this using the axios-auth-refresh package. I am posting the authProvider as an example.
// Function that will be called to refresh authorization
const refreshAuthLogic = () => {
const auth = getAuth();
if (auth && auth.refreshToken) {
const { refreshToken } = auth;

return axios
.post<IAuth>("/api/auth/refresh-token", {
refreshToken,
})
.then(({ data }) => {
setToken(data);

const { accessToken } = data;
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;

return Promise.resolve();
})
.catch(() => {
logout();
return Promise.reject();
});
}
logout();
return Promise.reject();
};

createAuthRefreshInterceptor(axios, refreshAuthLogic);
// Function that will be called to refresh authorization
const refreshAuthLogic = () => {
const auth = getAuth();
if (auth && auth.refreshToken) {
const { refreshToken } = auth;

return axios
.post<IAuth>("/api/auth/refresh-token", {
refreshToken,
})
.then(({ data }) => {
setToken(data);

const { accessToken } = data;
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;

return Promise.resolve();
})
.catch(() => {
logout();
return Promise.reject();
});
}
logout();
return Promise.reject();
};

createAuthRefreshInterceptor(axios, refreshAuthLogic);
You should follow this method in authProvider.checkAuth as well.
checkAuth: async () => {
// qs accessToken && refreshToken check
const params = new URLSearchParams(window.location.search);
const accessToken = params.get("accessToken");
const refreshToken = params.get("refreshToken");

if (accessToken && refreshToken) {
const { data, status } = await axios.get<IUser>(`/api/auth/me`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (status === 200) {
setToken({
...data,
accessToken,
refreshToken,
});
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;

// TODO: Must be better solution
window.location.href = "/";
return Promise.resolve();
}
}

const auth = getAuth();
if (auth) {
const { accessToken } = auth;
axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
return Promise.resolve();
}

return Promise.reject();
},
checkAuth: async () => {
// qs accessToken && refreshToken check
const params = new URLSearchParams(window.location.search);
const accessToken = params.get("accessToken");
const refreshToken = params.get("refreshToken");

if (accessToken && refreshToken) {
const { data, status } = await axios.get<IUser>(`/api/auth/me`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (status === 200) {
setToken({
...data,
accessToken,
refreshToken,
});
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${accessToken}`;

// TODO: Must be better solution
window.location.href = "/";
return Promise.resolve();
}
}

const auth = getAuth();
if (auth) {
const { accessToken } = auth;
axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
return Promise.resolve();
}

return Promise.reject();
},
We are preparing a sample document on this subject. We will publish it soon.
ratty-blush
ratty-blush577d ago
Hi @yildirayunlu thank you for your response. I have some question regarding refreshAuthLogic function you have created. In this function where you are catching an error from axios request how exactly is logout() function logging out the user. Can I know the logout code logic implementation? I am completely stuck on how can I log out the current user when the refresh token is expired.
Omer
Omer577d ago
The logout function only clears localstorage. The important point here is that Promise.reject is returned. So it falls into the checkError function of your authProvider. Then the user will be considered logged out already https://refine.dev/docs/api-reference/core/providers/auth-provider/#checkerror
ratty-blush
ratty-blush577d ago
Okay Thank you @Omer
Omer
Omer577d ago
It's works now?
ratty-blush
ratty-blush577d ago
I will test and let you know. Thank you @Omer and @yildirayunlu it is working now. Whenever the refresh token is expired user will be redirected to login page. But now I am facing a new issues. Whenever the user is logged out after the refresh token is expired they will be redirected to login page but the redirect url will be to=/login instead of redirect url as the resource that I am trying to access. For example: let us say I am trying to access the users resource from the menu but my refresh token is expired then I will be redirected to login page with url as <domain>/login?to=%2Flogin instead of <domain/login?to=%2Fusers I don't know if that's an issue or expected solution but the issue is when I am logged in and try to go to /login page I get 404 page not found message. Shouldn't the user should be redirected to dashboard or other page if they are logged in?
Omer
Omer577d ago
hmm let me check Do you have a chance to provide an environment where we can reproduce this issue? You can fork here, https://refine.dev/docs/examples/authentication/headless/
ratty-blush
ratty-blush577d ago
I will create it tomorrow if that's okay?
Omer
Omer577d ago
of course!
ratty-blush
ratty-blush575d ago
Refine Headless Example (forked) - StackBlitz
Run official live example code for Refine Headless, created by Refinedev on StackBlitz
ratty-blush
ratty-blush575d ago
Hello @Omer here is the exact code environment that I am using in my code-base.
Omer
Omer574d ago
Hey @dipbazz , Thank you! Let's take a look 👀 Hey @dipbazz , We were able to reproduce the issue. The solution seems a bit complicated. Can you open a GitHub issue? 👀
ratty-blush
ratty-blush569d ago
Hey @Omer I have created an issue and here is the link for the issue https://github.com/refinedev/refine/issues/2927
GitHub
[BUG] Issue on redirect url after user is logged out on jwt refresh...
Describe the bug I am using the JWT authentication on my project and it works fine when I log in and save the access token in my local or session storage. But whenever my refresh token is expired a...
Omer
Omer569d ago
Thank you! @dipbazz
optimistic-gold
optimistic-gold564d ago
Hi @dipbazz, https://github.com/refinedev/refine/issues/2927 Did you see my response the issue ? If the problem persists, please don't forget to report back. 🙏
ratty-blush
ratty-blush564d ago
Hi @alicanerdurmaz do I need to update refine-core in my package?
optimistic-gold
optimistic-gold564d ago
After PR is merged and released, yes you need do update patch version. But, when you make the changes I wrote, I believe your problem will be solved. I tested on your reproduced code. You don't need to wait release.
ratty-blush
ratty-blush564d ago
Thank you @alicanerdurmaz for your solution but it's still not working for me. Am I doing something wrong?
optimistic-gold
optimistic-gold564d ago
Oh, I'm sorry. Do you want to <domain>/login?to=/users am i right ? not <domain>/login Sorry for misunderstanding, Unfortunately you have to wait for the fix release. after release, u can do that with: https://refine.dev/docs/api-reference/core/providers/auth-provider/#redirection-after-error
checkError: (error) => {
if (error.response.status === 401) {
return Promise.reject('/users')
}
return Promise.resolve()
},
checkError: (error) => {
if (error.response.status === 401) {
return Promise.reject('/users')
}
return Promise.resolve()
},
ratty-blush
ratty-blush564d ago
Yeah that's what I would like to do. But can I have that custom-redirect-url to be dynamic. For eg: I was trying to access the resource posts and my refresh token got expired then I will be logged out and when I provide my credentials then now I should be redirected to /posts page. And same for the users resources as well.
optimistic-gold
optimistic-gold564d ago
I understood. when you mean dynamic, it should be redirected to <current resource> We will inform you when we release. thank you for clear explanation 🙏
ratty-blush
ratty-blush564d ago
Yeah exactly.
optimistic-gold
optimistic-gold564d ago
@dipbazz Hi again, we released fix. after npm i @pankod/refine-core@next you can try this:
checkError: (error) => {
if (error.response.status === 401) {
return Promise.reject(location.pathname)
}
return Promise.resolve()
},
checkError: (error) => {
if (error.response.status === 401) {
return Promise.reject(location.pathname)
}
return Promise.resolve()
},
refine core has not any opinion to redirect path after checkError is rejected. because we think this is app spesific feature and may vary. let me now if anything goes wrong 🙏
ratty-blush
ratty-blush564d ago
Hey @alicanerdurmaz I will test it tomorrow and let you know if something goes wrong. And BTW can I use this @next in production or just to test if it works?
optimistic-gold
optimistic-gold564d ago
next tag is currently @pankod/refine-core@3.88.1 and includes only logout logic changes https://github.com/refinedev/refine/compare/master...%40pankod/refine-core%403.88.1?diff=split our tests and example repos are working fine. I don't think it will be a problem for other parts of app but, better safe than sorry 🙏 😀
ratty-blush
ratty-blush563d ago
Thank you @alicanerdurmaz it's now working as expected.
optimistic-gold
optimistic-gold563d ago
I'm glad to hear that 🚀
More Posts
Custom Graphql query inputsHello, Can someone please explain to me how to write custom Graphql queries? We are using the PothAuzure AD B2C for Authentication and AuthorizationHi All, I am trying to set up auth with Azure AD. After a successful login on Azure AD B2C, I am redqueryOptionsI want to hit an Api with a specific route, for exampe, lets say api/user-directories/ for example Add smooth scroll effect in iconsHey everyone I want to made some changes in footer section of the this website but I'm not able to fHide *Button for forbidden actionsHi! Is there a way to avoid the disabled button for actions an user cannot do (because unauthorized)difference between importing from refinecore and refineantdfor example i have a component useSelect so if i import it from refinecore it dont have same props cI am getting 301 status using refine-simple-rest although there is not any error on postman.I am using refine-simple-rest as my data provider to connect with the django rest API but I get backchange label of breadcrumbi am using breadcrumb in create page but i am getting its as patients/create i want to change labeluseSelect onSearch without call API cos i got limit call APIcan i call API once and search query without recall API?How to submit a modal form to create a Comment when you are in the Post show page?Given the fact that I'm a first timer using React, Building refine app has been amazing. What I'd liMaking duplicate calls in the apiUsing the github example (finefoods - mui), the application is making duplicate calls in the api Docustom route element not visible{ element:<div> <LayoutWrapper>sssshhhhh</LayoutWrapper> </divpagination not working if i use filtered value and onfilter props in ant table columnfilteredValue={[consultationState]} onFilter={(value:any,record:any)=>{ return recorduseform-metadataBut the error is coming from the UserWithRolesRemove useModal form titleI want to remove title from useModalForm so i gave title ={null } its not workinguseExport with arrays in schemaI have resources that have array fields, which I am spreading to fields like `arrayItem.0.name, arrastartswith operatorstartswith operator not supportedediting usemodal form footeri am having a custom button in antmodal footer footer={<Button >okk</Button>} if i click on this sasetFilters for json objectis there a way to apply nested filters for json b object retuning from usetable , using setFiltersfiltering ant table based on useSelect hookconsider i have select dropdown where i have values as one, two and three . Below i have ant table w