You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
hiding a tab for long periods of time should automatically refresh the authentication token every time, allowing the user to easily return to his work whenever he wishes
Actual behaviour
hiding a tab for long periods of time should eventually may lead to Chrome heavily throttling scheduled tasks for up to a minute, eventually delaying the refresh of a token until it's no longer valid and logging out the user from the admin dashboard and potentially causing loss of work
How to reproduce
Make sure the frosh/tools package is installed¹
Open a new incognito window in your browser and navigate to the admin dashboard
Log in into the admin dashboard, selecting the Stay logged in option
Open a new empty tab in the same window, focus it and minimize the window for 30 minutes to an hour
Come back to see you've been logged out
This does involve a bit of chance, so might need a few tries to reproduce the error, but you'll easily notice through the network tab that the token is being renewed every 10 minutes instead of the expected 5 minutes.
¹ I suggest installing frosh/tools because it's the easiest and quickest way I found to reproduce, but this can surely be reproduced with any plugin or piece of code that regularly calls loginService.isLoggedIn() inside a scheduled call. See root cause below for why.
Root cause
This is two-fold:
In version 88, Chrome introduced Heavy throttling of chained JS timers, which means under certain conditions it is possible that a scheduled task (setTimeout/setInterval) is delayed for up to a whole minute.
Normally, this wouldn't be a problem, but means that if you had some plugin or other code repeatedly checking for loginService.isLoggedIn(), it would keep cancelling the refresh token task and delaying it, little by little, until the task is scheduled for less than a minute into the future. Put that repeating code inside a setInterval or chained setTimeout and you've met the criteria for heavy throttling, which might cause the refresh attempt to be delayed for up to a minute, until after the token is no longer valid. frosh/tools does just that inside its health status utility. Every 60s it checks if loginService.isLoggedIn() and performs some actions.
Please note though that this is not necessarily an issue with frosh/tools as this is a rather common pattern, but rather a bug in how Shopware core handles the auto renewal. The issue could occur with less frequent calls too as the core code also uses chained timers. It's just a lot more noticeable with it. Here's an example timeline:
0m00s: token is generated, expires in 10 minutes; refresh is scheduled for 5m00s
0m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 5m15s
1m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 5m45s
2m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 6m15s
3m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 6m45s
4m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 7m15s
5m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 7m45s
6m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 8m15s
7m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 8m45s
8m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 9m15s
9m30s: loginService.isLoggedIn() is called; refresh is rescheduled for 9m45s
10m30s: refresh happens only now because of throttling; refresh token is no longer valid so user is logged out
Possible solution
The easiest solution is to just avoid scheduling the renewal to less than a minute before the token expires. The following patch does just that. Let me know if that's an appropriate solution and if so I can open a PR.
diff --git a/src/Administration/Resources/app/administration/src/core/service/login.service.ts b/src/Administration/Resources/app/administration/src/core/service/login.service.ts
index 51cca3a7..ca67f2ea 100644
--- a/src/Administration/Resources/app/administration/src/core/service/login.service.ts+++ b/src/Administration/Resources/app/administration/src/core/service/login.service.ts@@ -267,9 +267,17 @@ export default function createLoginService(
const timeUntilExpiry = expiryTimestamp * 1000 - Date.now();
+ // in Chrome, and possibly other browsers, timeouts may be delayed for up to 1 minute in+ // inactive tabs, so we refresh it immediately if it's less than 1m15s away, to be safe+ // https://developer.chrome.com/blog/timer-throttling-in-chrome-88+ if (timeUntilExpiry < 75000) {+ void refreshToken();+ return;+ }+
autoRefreshTokenTimeoutId = setTimeout(() => {
void refreshToken();
- }, timeUntilExpiry / 2);+ }, timeUntilExpiry - 75000);
}
/**
The text was updated successfully, but these errors were encountered:
PHP Version
8.1.13
Shopware Version
6.5.8.4
Expected behaviour
hiding a tab for long periods of time should automatically refresh the authentication token every time, allowing the user to easily return to his work whenever he wishes
Actual behaviour
hiding a tab for long periods of time should eventually may lead to Chrome heavily throttling scheduled tasks for up to a minute, eventually delaying the refresh of a token until it's no longer valid and logging out the user from the admin dashboard and potentially causing loss of work
How to reproduce
frosh/tools
package is installed¹This does involve a bit of chance, so might need a few tries to reproduce the error, but you'll easily notice through the network tab that the token is being renewed every 10 minutes instead of the expected 5 minutes.
¹ I suggest installing
frosh/tools
because it's the easiest and quickest way I found to reproduce, but this can surely be reproduced with any plugin or piece of code that regularly callsloginService.isLoggedIn()
inside a scheduled call. See root cause below for why.Root cause
This is two-fold:
setTimeout
/setInterval
) is delayed for up to a whole minute.loginService.isLoggedIn()
is called, the timeout for the auto token refresh is deleted and a new one is created, scheduled to happen in half the time left for the refresh token expiration. This can be seen in https://github.com/shopware/shopware/blob/trunk/src/Administration/Resources/app/administration/src/core/service/login.service.ts#L412.Normally, this wouldn't be a problem, but means that if you had some plugin or other code repeatedly checking for
loginService.isLoggedIn()
, it would keep cancelling the refresh token task and delaying it, little by little, until the task is scheduled for less than a minute into the future. Put that repeating code inside asetInterval
or chainedsetTimeout
and you've met the criteria for heavy throttling, which might cause the refresh attempt to be delayed for up to a minute, until after the token is no longer valid.frosh/tools
does just that inside its health status utility. Every 60s it checks ifloginService.isLoggedIn()
and performs some actions.Please note though that this is not necessarily an issue with
frosh/tools
as this is a rather common pattern, but rather a bug in how Shopware core handles the auto renewal. The issue could occur with less frequent calls too as the core code also uses chained timers. It's just a lot more noticeable with it. Here's an example timeline:loginService.isLoggedIn()
is called; refresh is rescheduled for 5m15sloginService.isLoggedIn()
is called; refresh is rescheduled for 5m45sloginService.isLoggedIn()
is called; refresh is rescheduled for 6m15sloginService.isLoggedIn()
is called; refresh is rescheduled for 6m45sloginService.isLoggedIn()
is called; refresh is rescheduled for 7m15sloginService.isLoggedIn()
is called; refresh is rescheduled for 7m45sloginService.isLoggedIn()
is called; refresh is rescheduled for 8m15sloginService.isLoggedIn()
is called; refresh is rescheduled for 8m45sloginService.isLoggedIn()
is called; refresh is rescheduled for 9m15sloginService.isLoggedIn()
is called; refresh is rescheduled for 9m45sPossible solution
The easiest solution is to just avoid scheduling the renewal to less than a minute before the token expires. The following patch does just that. Let me know if that's an appropriate solution and if so I can open a PR.
The text was updated successfully, but these errors were encountered: