fix(bd-jv5u083l): renewal verdict deadline unwedges expiry logout#270
Merged
Conversation
cscheid
reviewed
Jun 10, 2026
When the IdP never calls back (GIS blocked / FedCM), isRefreshing stayed true for the tab's lifetime: the at-exp re-check rescheduled forever without a verdict and refocus/retry were disabled. A 30s deadline now settles the flag so the expiry check can log out (or keep waiting on network errors).
9b5422f to
9cd290e
Compare
…ilure path refreshTimer/expiryTimer were only used inside the schedule effect — effect-local variables replace the refs and the redundant top-of-effect clears (cleanup always runs before re-run). The verdict deadline and onError shared a settle+disable sequence, now abandonRenewal. No behavior change; existing tests unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
How the hub client manages session expiry today (
useAuth):GET /auth/mechecks whether the auth cookie is valid; a 401 shows the login screen.POST /auth/refresh./auth/me. A definitive 401 logs the user out (with a "session expired" message); a network error keeps the session and re-checks every 60 seconds — so offline editing survives expiry, and the dead session is adjudicated as soon as the hub is reachable again.isRefreshingflag so that concurrent triggers coalesce, the tab-refocus check stands down, and the expiry re-check waits for the renewal's verdict instead of logging the user out underneath it.The bug
Step 4 assumed the identity provider always answers. It doesn't: if the One Tap prompt never fires (script blocked by an ad blocker, FedCM / third-party-cookie policies, unregistered origin — we've observed
gsi/status403 in dev), neither the success nor the error callback ever runs, andisRefreshingstays true for the lifetime of the tab. Everything gated on that flag then silently dies:triggerRefresh()is a no-op — no retry, ever.So in exactly the environments where silent renewal can't work, the client neither renews nor logs out, until a full page reload or a websocket drop.
The fix
A 30-second verdict deadline on the in-flight renewal. If the provider hasn't settled by then, the renewal counts as failed and the flag clears, so the expiry re-check reaches its verdict normally: 401 → logout, network error → keep waiting.
The regression test mounts a session, lets the renewal trigger, and never settles it; it fails on
main(the session survives expiry) and passes with the fix.