[BUG] Can't login success after session expired(loop in authProvider onError)
Describe the bug
I upgrade refine from 4.x to 5.x. In new 5.x version, I found this bug. When my web session is expired and the server return 401, then refine redirect to login page.But I can't log in unless I refresh the login page。Because the refine loop in reminding me 401 error when I log in again,even the login interface returns success。
Steps To Reproduce
1.login in 2.go to A menu 3. delete cookie in brower 4. go to B menu 5. now will redirect to login 6. login again will failed because always call authProvider.onError
Expected behavior
login success after session expired(
Packages
"@refinedev/antd": "^6.0.1",
"@refinedev/cli": "^2.16.48",
"@refinedev/core": "^5.0.1",
"@refinedev/devtools": "^2.0.1",
"@refinedev/kbar": "^2.0.0",
"@refinedev/react-router": "^2.0.0",
"@refinedev/simple-rest": "^6.0.0",
Additional Context
No response
<html>
<body>
<!--StartFragment-->
onError | @ | authProvider.ts:115
-- | -- | --
| fn | @ | chunk-UJSJH2WS.js?v=735b857f:5426
| run | @ | chunk-UJSJH2WS.js?v=735b857f:4220
| start | @ | chunk-UJSJH2WS.js?v=735b857f:4263
| execute | @ | chunk-UJSJH2WS.js?v=735b857f:5462
| await in execute | |
| mutate | @ | chunk-UJSJH2WS.js?v=735b857f:5782
| (匿名) | @ | chunk-UJSJH2WS.js?v=735b857f:6438
| U | @ | chunk-UJSJH2WS.js?v=735b857f:8792
| commitHookEffectListMount | @ | chunk-WUVWK3JO.js?v=735b857f:16936
| commitPassiveMountOnFiber | @ | chunk-WUVWK3JO.js?v=735b857f:18184
| commitPassiveMountEffects_complete | @ | chunk-WUVWK3JO.js?v=735b857f:18157
| commitPassiveMountEffects_begin | @ | chunk-WUVWK3JO.js?v=735b857f:18147
| commitPassiveMountEffects | @ | chunk-WUVWK3JO.js?v=735b857f:18137
| flushPassiveEffectsImpl | @ | chunk-WUVWK3JO.js?v=735b857f:19518
| flushPassiveEffects | @ | chunk-WUVWK3JO.js?v=735b857f:19475
| commitRootImpl | @ | chunk-WUVWK3JO.js?v=735b857f:19444
| commitRoot | @ | chunk-WUVWK3JO.js?v=735b857f:19305
| performSyncWorkOnRoot | @ | chunk-WUVWK3JO.js?v=735b857f:18923
| flushSyncCallbacks | @ | chunk-WUVWK3JO.js?v=735b857f:9135
| (匿名) | @ | chunk-WUVWK3JO.js?v=735b857f:18655
| setTimeout | |
| systemSetTimeoutZero | @ | chunk-UJSJH2WS.js?v=735b857f:3677
| flush | @ | chunk-UJSJH2WS.js?v=735b857f:4038
| batch | @ | chunk-UJSJH2WS.js?v=735b857f:4056
| dispatch_fn | @ | chunk-UJSJH2WS.js?v=735b857f:4685
| setData | @ | chunk-UJSJH2WS.js?v=735b857f:4356
| fetch | @ | chunk-UJSJH2WS.js?v=735b857f:4578
| await in fetch | |
| executeFetch_fn | @ | chunk-UJSJH2WS.js?v=735b857f:5072
| onSubscribe | @ | chunk-UJSJH2WS.js?v=735b857f:4768
| subscribe | @ | chunk-UJSJH2WS.js?v=735b857f:3598
| (匿名) | @ | chunk-UJSJH2WS.js?v=735b857f:6356
| subscribeToStore | @ | chunk-WUVWK3JO.js?v=735b857f:12004
| commitHookEffectListMount | @ | chunk-WUVWK3JO.js?v=735b857f:16936
| commitPassiveMountOnFiber | @ | chunk-WUVWK3JO.js?v=735b857f:18184
| commitPassiveMountEffects_complete | @ | chunk-WUVWK3JO.js?v=735b857f:18157
| commitPassiveMountEffects_begin | @ | chunk-WUVWK3JO.js?v=735b857f:18147
| commitPassiveMountEffects | @ | chunk-WUVWK3JO.js?v=735b857f:18137
| flushPassiveEffectsImpl | @ | chunk-WUVWK3JO.js?v=735b857f:19518
| flushPassiveEffects | @ | chunk-WUVWK3JO.js?v=735b857f:19475
| performSyncWorkOnRoot | @ | chunk-WUVWK3JO.js?v=735b857f:18896
| flushSyncCallbacks | @ | chunk-WUVWK3JO.js?v=735b857f:9135
| commitRootImpl | @ | chunk-WUVWK3JO.js?v=735b857f:19460
| commitRoot | @ | chunk-WUVWK3JO.js?v=735b857f:19305
| finishConcurrentRender | @ | chunk-WUVWK3JO.js?v=735b857f:18833
| performConcurrentWorkOnRoot | @ | chunk-WUVWK3JO.js?v=735b857f:18746
| workLoop | @ | chunk-WUVWK3JO.js?v=735b857f:197
| flushWork | @ | chunk-WUVWK3JO.js?v=735b857f:176
| performWorkUntilDeadline | @ | chunk-WUVWK3JO.js?v=735b857f:384
<!--EndFragment-->
</body>
</html>
I print the server response.headers.date, you can see it print 8 times with same date value.( The number of lines of code may be incorrect because I deleted some irrelevant code)
loop in if (error.response?.status === 401) even if server return http 200 OK. onError triggered many times.
And refine always notify ‘Error (status code: 401)’ even if server return 200.
import type { AuthProvider } from "@refinedev/core";
import { MyKey, UserInfo } from "../interfaces";
import { v5 as uuidv5 } from "uuid";
import Cookies from "js-cookie";
export const authProvider = (apiUrl: string): AuthProvider => {
return {
login: async ({ username, email, password, providerName}) => {
const loginUrl = `${apiUrl}/login/signin`;
const response = await fetch(
loginUrl,
{
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json",
},
},
);
const http_response = await response.json();
if (http_response.code === 10000 && http_response.data.token) {
localStorage.setItem(MyKey.IAM_TOKEN, http_response.data.token);
localStorage.setItem(MyKey.IAM_USERINFO, JSON.stringify(http_response.data.userInfo));
return {
success: true,
redirectTo: "/",
};
}
return {
success: false,
error: {
name: "LoginError",
message: "Invalid username or password",
},
};
},
logout: async () => {
localStorage.removeItem(MyKey.IAM_TOKEN);
localStorage.removeItem(MyKey.IAM_USERINFO);
Cookies.remove("sessionId", { path: "/" });
return {
success: true,
redirectTo: "/login",
};
},
check: async () => {
const token = localStorage.getItem(MyKey.IAM_TOKEN);
if (token) {
return {
authenticated: true,
};
}
return {
authenticated: false,
redirectTo: "/login",
};
},
getPermissions: async () => null,
getIdentity: async () => {
const userInfoString = localStorage.getItem(MyKey.IAM_USERINFO);
if (userInfoString) {
let userInfo: UserInfo = JSON.parse(userInfoString);
return {
id: userInfo.username,
name: userInfo.displayName,
emailAddress: userInfo.emailAddress,
groups: userInfo.groups,
};
}
return null;
},
onError: async (error) => {
//console.error("authProvider: " + error);
console.error("authProvider: " + error.response.headers.date);
if (error.response?.status === 401) {
return {
logout: true,
};
}
return { error };
},
};
};
Hello @huntkalio can you update the latest versions and try again? If not, we appreciate if you can create a repository with reproduced issue.
Hello @huntkalio can you update the latest versions and try again? If not, we appreciate if you can create a repository with reproduced issue.
@BatuhanW after update, the problem is not resolved.The reproduced code can see in https://github.com/huntkalio/refine-test
1.fisrt login, then goto blog-posts page 2.f12 and delete brower cookie name 'sessionIdxx' 3.go to categories pages by click on categories menu 4.then will goto login page 5.then click login button,will not loggin
https://github.com/user-attachments/assets/4d9e241b-3f05-4b09-a0dd-f7280cc80b0c
Hey @huntkalio, I couldn't replicate the issue with the repo you provided.
https://github.com/user-attachments/assets/16a95cae-322c-4f8a-b695-2916941d7b77
I tried the following after that
npm ls @refinedev/core @refinedev/antd @refinedev/react-router @refinedev/simple-rest- the installed versions match exactly what is in the pkg-json
- `npm ci
- no difference in app behaviour from
npm i
- no difference in app behaviour from
The issue may be how your browser handles the cookie. I'm on Chrome v140.
@arndom This is a sporadic issue, but it's quite common if you click quickly enough. I've reproduced this issue on both Chrome 141.0.7390.66 and Firefox.When the login fails, the browser does not request https://mock.httpstatus.io/401?_end=10&_start=0 again, but the 401 error keeps popping up.
https://github.com/user-attachments/assets/35bdc4f7-9c21-4187-98dd-87ab40d33dce
https://github.com/user-attachments/assets/7439f7a5-da3f-46a8-b73f-bdd8cb1debe7
are you lot using a monorepo?
@smashah no
@huntkalio I was finally able to reproduce this on Chrome v141 and on another system with v140(no idea why it didn't throw prev.)
I'll see if I can find the cause of the issue.
@huntkalio The bug seems to be a result of the move to TanStack Query v5...useList now caches differently as it is on top of useQuery.
Previously in v4, when there is a cached error state, once a component that accesses that query key is remounted, there is a refetch. In v5, there is no refetch; the cached error state gets sent back. So now we have to either explicitly refetch or invalidate the query on login.
It took a while to figure out, but using React Query devtools helped me see what was being sent.
@BatuhanW is this something that should be handled by refine in auth providers, or does it become the user's job?
I'm having the same issue even though the server returns 200 on the query.
@arndom Thanks for finding the bug. I think this should be fixed within the Refine auth provider system. Would you like to work on this, or should I take a look?
@alicanerdurmaz could you point me in the right direction?
I intended to invalidate all queries on logout, but I'm stuck on accessing the current queryClient because it's defined within the <Refine> component, and it also depends on the component's props. Here
Hi @arndom, I think you can use useQueryClient hook to get the client.
yh, that should work. I was wrongly thinking of how to call the hook in the authProvider object, overlooking calling in the provider itself🤦♂️.