nhost-js-sdk icon indicating copy to clipboard operation
nhost-js-sdk copied to clipboard

JWT Refresh across multiple tabs

Open alaister opened this issue 3 years ago • 11 comments

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

alaister avatar Mar 27 '21 10:03 alaister

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.

elitan avatar Mar 27 '21 10:03 elitan

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.

alaister avatar Mar 27 '21 10:03 alaister

That could work. Or a simple boolean flag indicating if a refresh token request is in transit or not?

elitan avatar Mar 27 '21 10:03 elitan

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.

alaister avatar Mar 27 '21 10:03 alaister

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! 👍

elitan avatar Mar 27 '21 11:03 elitan

I had ran into it with just two tabs! Thanks for your time!

alaister avatar Mar 27 '21 11:03 alaister

Any update or workaround for this? This is a critical problem for my project.

noverby avatar Aug 14 '21 10:08 noverby

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 avatar Aug 15 '21 09:08 elitan

@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 avatar Aug 24 '21 19:08 noverby

@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!

Svarto avatar Aug 24 '21 19:08 Svarto

@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?

Svarto avatar Aug 29 '21 05:08 Svarto