Proposal: first-class Convex auth recovery (rebind after AuthenticationManager reaches terminal noAuth)
The problem
ConvexReactClient's internal AuthenticationManager reaches a terminal noAuth state after one failed token refetch. This is easy to hit in real apps: a mobile device or laptop wakes from sleep, the network is not back yet, the scheduled token refetch fails once, and the connection permanently downgrades. From then on every query fails with AUTHENTICATION_REQUIRED even though the auth provider holds (or can mint) a perfectly valid token.
The only way out is a fresh client.setAuth(...) call. But ConvexProviderWithAuth calls setAuth from an effect whose dependencies stay stable through this incident, so nothing ever rebinds. The app is stuck: valid session in the auth store, dead Convex connection, no supported API to reconnect the two.
Since kitcn already wraps ConvexProviderWithAuth and owns the auth store (AuthStateSync, useSafeConvexAuth, useFetchAccessToken), it is the natural home for a supported recovery path.
What we do today (the workaround kitcn could absorb)
We ship the same ~130-line rebind module in two apps (web + React Native), plus a recovery orchestrator on top:
- Capture the binding: wrap
client.setAuth once at client creation (WeakMap keyed by client) to remember the latest (fetchToken, onChange, onRefreshChange) binding that ConvexProviderWithAuth installs. setAuth replaces the onChange callback that drives useConvexAuth(), so recovery cannot just call setAuth with its own callback without freezing the provider's auth state.
- Rebind on demand: replay the captured binding through the original (unwrapped)
setAuth, preferring a live token fetcher over the captured one (the captured binding can hold a frozen-token closure from an initial-token bootstrap). Forward every auth-state change back to the provider's own onChange so useConvexAuth() stays live.
- Verify: wait for the backend to confirm authentication (bounded by a timeout), then let the caller settle its recovery state machine (idle / recovering / failed with capped exponential backoff and dedupe across triggers).
This works, but it monkey-patches client.setAuth and depends on convex/react internals staying shaped the way they are. Every app that hits the wake-from-sleep incident has to rediscover and reimplement all of this.
Proposal
A kitcn-owned recovery feature, roughly:
trackAuthBinding(client) behavior built into kitcn's provider setup (it already owns client wiring), so the latest setAuth binding is always captured.
- An imperative
useConvexAuthRecover() (or client-level recoverAuth()) that rebinds using the live useFetchAccessToken fetcher and resolves when the backend confirms auth.
- Optionally, the shared recovery latch: dedupe concurrent triggers (query errors, app-foreground events, session reconcilers), capped backoff, and a
status observable (idle | recovering | failed) apps can render from.
The pieces are dependency-free TypeScript with injected callbacks and we are happy to contribute our implementation (module + ~430 lines of tests, battle-tested across seven production iterations) as a starting point if there is interest.
Longer term the root cause belongs upstream in convex/react (a supported rebind, or a non-terminal noAuth state), but kitcn wrapping it would already remove the fragile monkey-patch from userland today.
Happy to open a PR. Cheers,
Diego
Proposal: first-class Convex auth recovery (rebind after AuthenticationManager reaches terminal noAuth)
The problem
ConvexReactClient's internalAuthenticationManagerreaches a terminalnoAuthstate after one failed token refetch. This is easy to hit in real apps: a mobile device or laptop wakes from sleep, the network is not back yet, the scheduled token refetch fails once, and the connection permanently downgrades. From then on every query fails withAUTHENTICATION_REQUIREDeven though the auth provider holds (or can mint) a perfectly valid token.The only way out is a fresh
client.setAuth(...)call. ButConvexProviderWithAuthcallssetAuthfrom an effect whose dependencies stay stable through this incident, so nothing ever rebinds. The app is stuck: valid session in the auth store, dead Convex connection, no supported API to reconnect the two.Since kitcn already wraps
ConvexProviderWithAuthand owns the auth store (AuthStateSync,useSafeConvexAuth,useFetchAccessToken), it is the natural home for a supported recovery path.What we do today (the workaround kitcn could absorb)
We ship the same ~130-line rebind module in two apps (web + React Native), plus a recovery orchestrator on top:
client.setAuthonce at client creation (WeakMap keyed by client) to remember the latest(fetchToken, onChange, onRefreshChange)binding thatConvexProviderWithAuthinstalls.setAuthreplaces theonChangecallback that drivesuseConvexAuth(), so recovery cannot just callsetAuthwith its own callback without freezing the provider's auth state.setAuth, preferring a live token fetcher over the captured one (the captured binding can hold a frozen-token closure from an initial-token bootstrap). Forward every auth-state change back to the provider's ownonChangesouseConvexAuth()stays live.This works, but it monkey-patches
client.setAuthand depends onconvex/reactinternals staying shaped the way they are. Every app that hits the wake-from-sleep incident has to rediscover and reimplement all of this.Proposal
A kitcn-owned recovery feature, roughly:
trackAuthBinding(client)behavior built into kitcn's provider setup (it already owns client wiring), so the latestsetAuthbinding is always captured.useConvexAuthRecover()(orclient-levelrecoverAuth()) that rebinds using the liveuseFetchAccessTokenfetcher and resolves when the backend confirms auth.statusobservable (idle | recovering | failed) apps can render from.The pieces are dependency-free TypeScript with injected callbacks and we are happy to contribute our implementation (module + ~430 lines of tests, battle-tested across seven production iterations) as a starting point if there is interest.
Longer term the root cause belongs upstream in
convex/react(a supported rebind, or a non-terminal noAuth state), but kitcn wrapping it would already remove the fragile monkey-patch from userland today.Happy to open a PR. Cheers,
Diego