fix(shell): harden account menu sign out#365
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR replaces Clerk's built-in
Confidence Score: 5/5Safe to merge; the sign-out hardening is well-structured with timeout guards on both the Matrix session clear and the Clerk call, and the test suite covers the two-phase flow, non-OK cleanup responses, and the re-enable-on-timeout path. The Matrix session clear has proper error handling, and the Clerk sign-out is now bounded. The one edge case — a post-timeout Clerk signOut that eventually completes and triggers its own redirect — is a minor UX surprise rather than a correctness or security issue, and it was intentionally left as an acceptable orphan state in the PR description. shell/src/components/UserButton.tsx — specifically the clerkSignOutWithTimeout post-timeout dangling call path. Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User
participant B as UserButton
participant M as /api/auth/app-session
participant C as Clerk
U->>B: Click "Sign out"
B->>B: setSigningOut(true)
B->>M: DELETE (AbortController, 10s timeout)
alt Matrix session cleared OK
M-->>B: 200 OK
else Non-OK response
M-->>B: 4xx/5xx → console.warn + continue
else Timeout/network error
M-->>B: AbortError/NetworkError → console.warn + continue
end
B->>C: "signOut({ redirectUrl }) via Promise.race (10s timeout)"
alt Clerk sign-out succeeds
C-->>B: resolved → Clerk navigates to redirectUrl
else Clerk sign-out times out
B->>B: setSigningOut(false) → button re-enabled
Note over B,C: in-flight signOut() still running (may redirect later)
else Clerk sign-out fails (non-timeout)
B->>B: setSigningOut(false)
B->>U: window.location.replace(redirectUrl)
end
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
shell/src/components/UserButton.tsx:55-69
**Dangling Clerk `signOut()` call after the timeout**
`Promise.race` stops waiting for the `signOut` promise when the 10 s timeout fires, but the underlying Clerk call is still in-flight. Clerk's `signOut({ redirectUrl })` handles its own navigation internally — if it eventually settles after the button has been re-enabled, it will navigate the user to the sign-in page from a state they perceive as a "retry" window. The test covers the re-enable path but mocks `signOut` as a permanently-pending promise, so the post-timeout settlement scenario is not exercised. There is no way to cancel the Clerk call once started, but wrapping the settled case in a checked flag (`let signedOut = false` set before the race, checked in a `.then()` on the original promise) would allow you to no-op the navigation if the race was already lost to the timeout.
Reviews (2): Last reviewed commit: "fix(shell): bound account sign out failu..." | Re-trigger Greptile |
Summary
Tests
flox activate -- bun run test -- tests/shell/user-button.test.tsx tests/shell/menu-bar-focus.test.tsx tests/shell/settings-panel.test.tsx tests/shell/billing-gate.test.tsx tests/shell/mobile-shell.test.tsx tests/shell/user-button-hydration.test.tsxflox activate -- bun run typecheckflox activate -- npx react-doctor@latest shell --diff --verboseflox activate -- bun run check:patternsReview/Monitoring
fix(shell): bound account sign out failures.Invariants
/api/auth/app-sessionclears Matrix's platform session cookie for routed shell access.