fix(auth): report a rotation that never reached the config - #116
Merged
Conversation
A refresh that the server accepts but the config write skips is the worst of both worlds: the server has already invalidated the token still held in memory, so the next refresh presents a superseded one, reads as reuse, and revokes the whole family. The account slot going missing between resolving the account and writing the rotation back took that path silently, leaving a forced sign-out with nothing in the logs pointing at the cause. Track whether the mutator found the slot and write a line to stderr when it did not. Behavior is otherwise unchanged; this only makes the existing skip visible.
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.
Problem
onTokenRefreshwrites a freshly rotated token pair back into the account slot. When the slot is absent it returns early and says nothing.That silence is expensive. The server has already invalidated the token the process still holds, so the next refresh presents a superseded one. The server reads that as reuse, revokes the whole token family, and every client on that account is signed out. The operator sees a forced sign-out with nothing in any log that points at the write that was skipped.
We hit this in production on 2026-08-15: two accounts revoked 0.1s apart, both presenting a refresh token that had been rotated away 59 minutes earlier. Sync for both folders then stayed down for two days before anyone noticed. The server-side record is good (family id, gap, user agent), but nothing on the client says whether the rotation an hour earlier was ever saved, so the cause is still open.
Change
Track whether the mutator found the slot, and write one line to stderr when it did not:
Behavior is otherwise unchanged. This does not fix the revocation; it makes the suspected cause observable so the next occurrence is diagnosable.
Notes
isAuthLikeMessage, so a binding is not flipped to "sign in required". That is correct here: auth is still valid at this point.Test
test/load-sdk-client-multi.test.tsremoves the account slot after the interceptor resolves the account but before the rotation lands, then asserts stderr names the account and the env and does not leak the new token.Full suite: 666 tests pass, typecheck clean.