fix: improve auth session sync reliability#24
Conversation
- Add configurable JWT validation leeway (default 60s) to handle clock drift between Clerk servers and the backend - Catch ExpiredTokenError alongside InvalidClaimError/MissingClaimError to prevent crashes on expired tokens - Rewrite ClerkSessionSynchronizer JS for reliability: - Use useRef to deduplicate rapid calls while remaining reconnect-safe - Request fresh tokens with skipCache to avoid near-expiry cached tokens - Handle token retrieval failures gracefully (clear session instead of hang) - Include all dependencies in useEffect array ([isLoaded, isSignedIn, addEvents, getToken]) - Add unit tests for expired token handling and JS code correctness - Add pythonpath config for pytest to find custom_components
JS dedupe + retry fixes (Copilot review): - Only update lastSentRef after a confirmed dispatch so a failed token fetch doesn't poison the dedupe and block later retries. - Add inFlightRef to prevent overlapping getToken calls when the effect re-fires before the in-flight promise resolves. - Retry getToken once after a 500ms delay before clearing the backend session, so transient token-fetch failures don't force a backend logout while Clerk is still signed in. - On final failure, leave lastSentRef unchanged so the next trigger (reconnect, sign-in toggle) re-attempts the sync. Test typecheck fixes: - pyright ignore for the `_reflex_internal_init` Reflex internal init flag. - pyright ignore for accessing `.fn` on the wrapped EventCallback. Co-Authored-By: Paul Johnson <paul.johnson@snaplabs.ai> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Improves Clerk auth session synchronization reliability between the Reflex frontend and backend by hardening JWT validation and making the frontend synchronizer more reconnect- and failure-tolerant.
Changes:
- Add configurable JWT validation leeway (default 60s) and handle
ExpiredTokenErrorduring claim validation. - Rewrite
ClerkSessionSynchronizerJS to dedupe rapid triggers, request fresh tokens (skipCache), and handle token-fetch failures with retry/clear behavior. - Add unit tests for expired-token handling and JS regression checks; configure pytest
pythonpathfor local imports.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| custom_components/reflex_clerk_api/clerk_provider.py | Adds JWT leeway + expired-token handling; rewrites session sync JS logic and React imports. |
| tests/test_clerk_provider_unit.py | Adds regression/unit tests for leeway + expired-token path and generated JS content. |
| pyproject.toml | Adds pytest pythonpath to ensure custom_components is importable in tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const stateKey = isSignedIn ? "signed_in" : "signed_out" | ||
| if ( | ||
| lastSentRef.current?.stateKey === stateKey && | ||
| lastSentRef.current?.addEvents === addEvents | ||
| ) return | ||
| if (inFlightRef.current) return | ||
|
|
| .then(token => {{ | ||
| if (token) {{ | ||
| addEvents([ReflexEvent("{state}.set_clerk_session", {{token}})]) | ||
| lastSentRef.current = {{ stateKey, addEvents }} |
- Drop inFlightRef hard gate. It blocked the !isSignedIn branch, so a sign-out occurring during an in-flight token fetch would not dispatch clear_clerk_session and the backend session would stay stale until another trigger arrived. - Replace it with a requestIdRef counter that's incremented on every effect run with a new desired state. The in-flight fetch's then/catch handlers check the captured myRequestId against requestIdRef.current before dispatching - so a getToken() that resolves after sign-out can no longer dispatch a stale set_clerk_session. - Sign-out path now runs unconditionally and immediately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed both Copilot race-condition comments in the new commit: Comment 1 (sign-out blocked by
This fixes both races: sign-out can no longer be suppressed by an in-flight fetch, and a stale token-fetch result can no longer re-authenticate the backend after sign-out. |
Takeover of #22 by @PJ-Snap with review fixes applied.
Summary
ExpiredTokenErroralongsideInvalidClaimError/MissingClaimErrorto prevent crashes on expired tokensClerkSessionSynchronizerJS for reliability:useRefto deduplicate rapid calls while remaining reconnect-safeskipCacheto avoid near-expiry cached tokensuseEffectarraypythonpathconfig for pytest to findcustom_componentsReview feedback addressed (Copilot review on #22)
Dedupe + retry fixes in
clerk_provider.py:lastSentRefafter a confirmed dispatch so a failed token fetch doesn't poison the dedupe and block later retries.inFlightRefto prevent overlappinggetTokencalls when the effect re-fires before the in-flight promise resolves.getTokenonce after a 500 ms delay before clearing the backend session, so transient token-fetch failures don't force a backend logout while Clerk is still signed in.lastSentRefunchanged so the next trigger (reconnect, sign-in toggle, etc.) re-attempts the sync.Test typecheck fixes:
# pyright: ignore[reportCallIssue]for the_reflex_internal_initReflex internal init flag.# pyright: ignore[reportAttributeAccessIssue]for accessing.fnon the wrappedEventCallback.Test plan
uv run ruff check .passesuv run pyrightpasses (0 errors)uv run pytest tests/test_clerk_provider_unit.py -vpasses (2/2)Supersedes #22. Original work by @PJ-Snap.