fix(clerk-js): refresh session cookie on visibilitychange in cross-origin iframes#8923
fix(clerk-js): refresh session cookie on visibilitychange in cross-origin iframes#8923jacekradko wants to merge 1 commit into
Conversation
…frames The session-cookie refresh listened only to window 'focus', which never fires inside a cross-origin iframe on tab re-activation, and the cookie write was gated on document.hasFocus() (always false for an embedded frame). Embedded apps (e.g. a preview pane) could keep a stale token after tab-back and get 401s until a manual refresh. Now also refresh on visibilitychange (which reaches iframes) and let a visible cross-origin iframe write the cookie. Top-level multi-tab behavior is unchanged. Partition-aware clerk_active_context is tracked separately (#8921).
🦋 Changeset detectedLatest commit: 6e14da1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthrough
Changesiframe visibilitychange session cookie refresh
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/eslint-plugin
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
API Changes Report
Summary
No API Changes DetectedAll packages have stable APIs with no detected changes. Report generated by Break Check Last ran on |
Apps running inside a cross-origin iframe (Replit's preview pane is the reported case) could get 401s after the user tabbed away and came back, until a manual page refresh. clerk-js refreshed the session cookie only on the window
focusevent, which never fires inside a cross-origin iframe on tab re-activation, and it gated the cookie write ondocument.hasFocus(), which a visible-but-embedded frame never reports. So the token went stale and the app's refetch-on-focus hit it.This adds a
visibilitychangetrigger next to the existingfocusone (visibilitychange does propagate into iframes) and lets a visible cross-origin iframe write the refreshed cookie. The gate change is additive: top-level multi-tab ownership still keys ondocument.hasFocus(), so the active-organization handoff from #3786 is untouched. The subtlety worth a look is thetypeof document !== 'undefined'guard on the new listener. ThemockNativeRuntimepath constructsAuthCookieServicewith awindowbut nodocument, and an unguardeddocument.addEventListenerthrows there.The cross-origin focus/visibility semantics this relies on are pinned down by a real-browser test (
iframe-session-refresh.spec.ts): a visible cross-origin iframe reportshasFocus() === falseand only receivesfocuswhen explicitly clicked into. The behavioral wiring (refresh onvisibilitychangewhen visible-but-unfocused-in-iframe) is covered by newAuthCookieServiceunit tests, which had no coverage before. One honest caveat: the full "background the tab until the cookie expires, then watch it heal" leg cannot run in automated Chromium (it won't background a page, and the worker poller keeps the token fresh), so that end-to-end path is being verified manually against a real embedded app.Follow-up filed as #8921:
clerk_active_contextis not partition-aware, which makes the write-gate arbitration non-deterministic in CHIPS iframes. Left out of scope here.Summary by CodeRabbit
New Features
Tests