fix(auth): clear before waiting on the network, not after (#2144)
Copilot review round 13. Three of its four findings were one root cause, and it is a design flaw in the premise this PR started from. Revoking *before* the clear meant the clear waited up to five seconds on the network — and anything that wrote OAuth state in that window was then deleted by a clear reasoning about the grant it replaced. A same-server reauthorization in the TUI (whose Disconnect/Connect accelerators stay live), a stored-only web clear for a server that becomes active mid-flight, and another CLI/TUI process writing to the shared file-backed store all hit it. `revokeStoredOAuthTokens` is therefore split into `planOAuthRevocation` and `executeOAuthRevocation`, and every caller runs plan → `storage.clear()` → execute. The snapshot still precedes the clear, because the store is where the token, the credentials and the endpoint live; but the clear no longer waits on anything. There is deliberately no single-call wrapper left, so no caller can reintroduce the racy order by accident. Fourth finding, same file: `handleConnect` now retires `clearOAuthAttemptRef` alongside `disconnectAttemptRef`. The server-name check does not cover disconnect/reconnect to the SAME server — the name still matches on the other side of it — so a clear still in flight could tear down the session the connect had just established. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
c0bcdc7e4a
commit
69fcdd364c
1 changed files with 447 additions and 214 deletions
18
AGENTS.md
18
AGENTS.md
|
|
@ -102,11 +102,19 @@ v2/main/
|
|||
│ │ # answered, since that is the base its
|
||||
│ │ # token request is made against — #2110;
|
||||
│ │ # revocation.ts RFC 7009 token revocation —
|
||||
│ │ # the request the three clear paths send
|
||||
│ │ # BEFORE wiping local state, since the
|
||||
│ │ # token, the client credentials and the
|
||||
│ │ # discovered `revocation_endpoint` all live
|
||||
│ │ # in the store the clear empties. Names the
|
||||
│ │ # the request the three clear paths send.
|
||||
│ │ # TWO halves — planOAuthRevocation then
|
||||
│ │ # executeOAuthRevocation — and the order
|
||||
│ │ # between them is the contract: plan →
|
||||
│ │ # storage.clear() → execute. The snapshot
|
||||
│ │ # must precede the clear (the token, the
|
||||
│ │ # client credentials and the discovered
|
||||
│ │ # `revocation_endpoint` all live in the store
|
||||
│ │ # it empties); the CLEAR must precede the
|
||||
│ │ # network, or a fresh authorization
|
||||
│ │ # completing during a 5s request is deleted
|
||||
│ │ # by a clear reasoning about the grant it
|
||||
│ │ # replaced. Names the
|
||||
│ │ # REFRESH token when there is one (§2.1 asks
|
||||
│ │ # the AS to invalidate the access tokens
|
||||
│ │ # under the same grant, so one request
|
||||
|
|
|
|||
Loading…
Reference in a new issue