Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect isAuthenticated state in hook #1468

Open
mparsakia opened this issue Jan 18, 2025 · 2 comments
Open

Incorrect isAuthenticated state in hook #1468

mparsakia opened this issue Jan 18, 2025 · 2 comments

Comments

@mparsakia
Copy link

mparsakia commented Jan 18, 2025

Hi, we are facing a problem with the auth.isAuthenticated state while trying to coordinate updates to the auth over multiple tabs.

Our user store is in shared localStorage: userStore: new WebStorageStateStore({ store: window.localStorage }),

Since the library does not seem to be not subscribed to storage events, in order to not cause issues with multiple tabs hitting the silent renew endpoint at the same time we are using a BroadcastChannel to start and stop the silent renew service on backgrounded tabs when one tab performs a silent renewal, as to "re-load" the user (and have the library re-calculate exp timers).

In our system each instance (tab) is given a random time (in seconds) e.g. accessTokenExpiringNotificationTimeInSeconds: randomRefeshTime between 1-4 mins before refresh to try and reduce the chances of the multiple tabs hitting the refresh endpoint at the same time, which could cause other tabs to get an invalid grant response as the refresh token was already consumed.

The tab that performs the successful refresh will broadcast to other tabs to essentially perform auth.stopSilentRenew(); and then auth.startSilentRenew(); which, in the debug logging seems to re-load the user - I was not able to find another method on the hook to directly load a user that I could pass in.

However, we have a problem with this. This seems to work as far as loading the user into localStorage and correct the silent renewal timers, I can verify that the token is rotating properly across all tabs - but the actual auth.isAuthenticated and auth.user when accessed via const auth = useAuth(); does not seem to update when we perform this. This is causing problems for our application as we need to update the Bearer token for http requests when auth state changes, and we pull that from the user via the hook, as well as block certain actions based on the auth.isAuthenticated state.

It seems here that the auth hook's state should be updated on user loaded but I am not seeing this happen, despite the logging saying it has:

https://github.com/authts/react-oidc-context/blob/main/src/reducer.ts#L16-L26

// Refresh Broadcast - Token Expiring Refreshed 
[UserManager] _loadUser: user storageString loaded
Logger.ts:74 [User.fromStorageString] begin
Logger.ts:79 [UserManager] getUser: user loaded
Logger.ts:74 [UserManagerEvents] load: begin
Logger.ts:74 [UserManagerEvents] load: access token present, remaining duration: 300
Logger.ts:74 [UserManagerEvents] load: registering expiring timer, raising in 176 seconds
Logger.ts:74 [Timer('Access token expiring')] init: begin...

I can see the renewal timer being corrected on other tabs via the logging as well.

Only the tab that performed the silent sign-in has the correct auth.isAuthenticated state. The other tabs, although they claim to have re-loaded the user and restarted their timers, the auth.isAuthenticated and auth.user on those tabs is incorrect.

Is there any method or way we can tell the auth hook to try and re-read the user state and correct it's internal states?

@eladams
Copy link

eladams commented Feb 3, 2025

Same issue here 👍

@mparsakia
Copy link
Author

@eladams

It seems there is another method, calling await auth.events.load(...); and then stop and start silent renew seems to correct the useAuth hook states. I had some issues with the hook not actually updating if the access token is expired, it seems to only check authentication and not authorization.

However we had too many issues with the automatic silent renew options in this library, and ended up writing a custom solution for the renewal that checks if the token is expired in the fetch (eg axios interceptor) on demand and performs the silent sign-in there as needed. Through a combination of StorageEvent, Web Locks, and another JWT parsing library to check if the token is or nearly expired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants