nhost-js-sdk
nhost-js-sdk copied to clipboard
JWT Refresh across multiple tabs
Hi,
Does nhost-js-sdk
support refreshing JWTs across multiple tabs?
When I've got more than one tab open on my app it seems to log itself out constantly and I believe this is due to the refresh toking becoming out of sync across tabs.
Thanks
We don't sync refresh token request across tabs. You're probably experience a race condition.
How can we solve this?
One way would allow refresh tokens to be active for a few seconds after they've been used.
That's a possible solution, would it lead to multiple valid refresh tokens per "session" though? Another solution would require saving the JWT to localstorage but then you could use localstorage to sync browser tabs using localstorage events. You would have to introduce some "jitter" to your auto refresh timer so two browser tabs don't try and refresh at the same time.
That could work. Or a simple boolean flag indicating if a refresh token request is in transit or not?
I think that could work. So if the second tab tried to refresh the JWT while the first was refreshing, it could retry with the new refresh token retrieved from the first tab's refresh? The downside being more token refreshes (one per tab, every refresh) but the upside being you don't have to persist the JWT.
Ah now I understand the problem better. Each tab have a separate JWT token (in memory per tab) but they share the refresh token via localStorage.
How many tabs do you have open of the same application for this to be a problem? :D
I'll think about this problem during the weekend. If you have some more ideas just post it here! 👍
I had ran into it with just two tabs! Thanks for your time!
Any update or workaround for this? This is a critical problem for my project.
I'm able to have multiple tabs open at the same time without having this issue.
The only thing I can think about causing this issue would be some kind of race condition, similar to this:
https://github.com/nhost/nhost-js-sdk/blob/master/src/Auth.ts#L564-L567
I could do is to replace the internal variable with clientStorage
(localStoarge
in the browser) so the lock would be tied to all tabs in a browser.
Any other ideas on what would cause this?
@elitan it is definitely a race condition. I can only reproduce the issue, if I open 2 additional tabs (other than the original tab) at the same time.
The cause of the issue is that tab 2 and tab 3 tries to use the same refreshToken from tab 1 to run _refreshToken(). One of them will run httpClient.get("/token/refresh") first and be successful, and the other will fail since it uses the now outdated token from tab 1. When the other tab fails, it will trigger a logout.
I tried to create a locking solution with localStorage, as you suggested, but it does not work, since the locking variable is not properly synched between the tabs.
We would need to use IndexedDB to have a locking variable, that is properly synched between tabs.
@noverby Interesting, I've been debugging a similar error in React Native. When the app is backgrounded, and then put into a low battery mode / dormant state, when then opened again there's a double try sometimes with the same token causing a logout.
Been really hard to debug and get a reproducible example, but this can very well be a similar case so if this is solved then it should also solve the RN case!
@noverby @elitan I've been thinking about this. What about doing the edit on the backend that if the same token is received within a certain time window (e.g. 5 seconds) then the second request is ignored / silent fail.
If a 401 isn't sent back, then it won't cause a logout and the client can just try again with a different token?