fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
Conversation
When an app is loaded in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe (eTLD+1 probe fails due to third-party cookie restrictions), causing host-only cookies that conflict with domain-scoped cookies from the non-iframe context. Fall back to hostname instead of undefined so the cookie set either matches the non-iframe's domain-scoped cookie or silently fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b245c99 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.
|
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@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: |
📝 WalkthroughWalkthroughAdds a changeset for a patch release and updates getCookieDomain to accept optional cookieAttributes (sameSite, secure). The eTLD+1 probe now applies those attributes to set/remove calls and the function falls back to and caches the original hostname when no eTLD+1 is found instead of returning undefined. Tests were updated to assert the hostname fallback and that cookie attributes are forwarded to the probe. clientUat cookie logic now calls getCookieDomain with sameSite and secure attributes. 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
| // In restricted contexts (e.g. cross-origin iframes), the set() will silently | ||
| // fail — which is preferable to creating a host-only cookie that conflicts | ||
| // with domain-scoped cookies set by non-iframe contexts. | ||
| cachedETLDPlusOne = hostname; |
There was a problem hiding this comment.
Hmm. I think it makes sense, but I wonder if this could potentially set a cookie on a different subdomain that would still create 2 separate cookies in some cases. 🤔
The eTLD+1 probe in getCookieDomain() was using default cookie attributes (SameSite=Lax) while the actual __client_uat cookie uses SameSite=None; Secure in iframe contexts. This mismatch could cause the probe to fail at a domain level where the actual cookie set would succeed, potentially creating duplicate cookies on different subdomains. Now getCookieDomain() accepts optional cookie attributes that are passed through to the probe, and clientUat.ts forwards SameSite/Secure so the probe accurately reflects the actual cookie behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/clerk-js/src/core/auth/getCookieDomain.ts`:
- Around line 11-23: The exported function getCookieDomain lacks an explicit
TypeScript return type and its JSDoc is incomplete; update the declaration of
getCookieDomain to include an explicit return type (e.g., string | undefined)
and ensure cookieAttributes has precise types (sameSite?: 'Lax' | 'Strict' |
'None' | string; secure?: boolean), then expand the JSDoc for getCookieDomain to
include `@returns` describing the returned eTLD+1 or undefined, `@throws` describing
any errors thrown during the eTLD+1 probe (e.g., when cookie handling fails),
and an `@example` showing typical usage; reference the function name
getCookieDomain and the cookieAttributes param as the places to change.
| /** | ||
| * @param hostname - The hostname to determine the eTLD+1 for. | ||
| * @param cookieHandler - The cookie handler to use for the eTLD+1 probe. | ||
| * @param cookieAttributes - Optional cookie attributes (sameSite, secure) to use | ||
| * during the eTLD+1 probe. These should match the attributes that will be used | ||
| * when setting the actual cookie, so the probe accurately reflects whether a | ||
| * domain-scoped cookie can be set in the current context. | ||
| */ | ||
| export function getCookieDomain( | ||
| hostname = window.location.hostname, | ||
| cookieHandler = eTLDCookie, | ||
| cookieAttributes?: { sameSite?: string; secure?: boolean }, | ||
| ) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/clerk-js/src/core/auth/getCookieDomain.tsRepository: clerk/javascript
Length of output: 2746
Add explicit return type and complete JSDoc documentation for the exported getCookieDomain function.
This public API lacks an explicit return type annotation. Its JSDoc documentation is also incomplete, missing @returns, @throws, and @example tags required by the TypeScript and JSDoc guidelines.
Suggested patch
/**
* `@param` hostname - The hostname to determine the eTLD+1 for.
* `@param` cookieHandler - The cookie handler to use for the eTLD+1 probe.
* `@param` cookieAttributes - Optional cookie attributes (sameSite, secure) to use
* during the eTLD+1 probe. These should match the attributes that will be used
* when setting the actual cookie, so the probe accurately reflects whether a
* domain-scoped cookie can be set in the current context.
+ * `@returns` The resolved eTLD+1 domain to use for setting cookies.
+ * `@throws` If the cookie handler throws while probing domains.
+ * `@example`
+ * getCookieDomain('app.example.com', undefined, { sameSite: 'None', secure: true });
*/
export function getCookieDomain(
hostname = window.location.hostname,
cookieHandler = eTLDCookie,
cookieAttributes?: { sameSite?: string; secure?: boolean },
-) {
+): string {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * @param hostname - The hostname to determine the eTLD+1 for. | |
| * @param cookieHandler - The cookie handler to use for the eTLD+1 probe. | |
| * @param cookieAttributes - Optional cookie attributes (sameSite, secure) to use | |
| * during the eTLD+1 probe. These should match the attributes that will be used | |
| * when setting the actual cookie, so the probe accurately reflects whether a | |
| * domain-scoped cookie can be set in the current context. | |
| */ | |
| export function getCookieDomain( | |
| hostname = window.location.hostname, | |
| cookieHandler = eTLDCookie, | |
| cookieAttributes?: { sameSite?: string; secure?: boolean }, | |
| ) { | |
| /** | |
| * `@param` hostname - The hostname to determine the eTLD+1 for. | |
| * `@param` cookieHandler - The cookie handler to use for the eTLD+1 probe. | |
| * `@param` cookieAttributes - Optional cookie attributes (sameSite, secure) to use | |
| * during the eTLD+1 probe. These should match the attributes that will be used | |
| * when setting the actual cookie, so the probe accurately reflects whether a | |
| * domain-scoped cookie can be set in the current context. | |
| * `@returns` The resolved eTLD+1 domain to use for setting cookies. | |
| * `@throws` If the cookie handler throws while probing domains. | |
| * `@example` | |
| * getCookieDomain('app.example.com', undefined, { sameSite: 'None', secure: true }); | |
| */ | |
| export function getCookieDomain( | |
| hostname = window.location.hostname, | |
| cookieHandler = eTLDCookie, | |
| cookieAttributes?: { sameSite?: string; secure?: boolean }, | |
| ): string { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/clerk-js/src/core/auth/getCookieDomain.ts` around lines 11 - 23, The
exported function getCookieDomain lacks an explicit TypeScript return type and
its JSDoc is incomplete; update the declaration of getCookieDomain to include an
explicit return type (e.g., string | undefined) and ensure cookieAttributes has
precise types (sameSite?: 'Lax' | 'Strict' | 'None' | string; secure?: boolean),
then expand the JSDoc for getCookieDomain to include `@returns` describing the
returned eTLD+1 or undefined, `@throws` describing any errors thrown during the
eTLD+1 probe (e.g., when cookie handling fails), and an `@example` showing typical
usage; reference the function name getCookieDomain and the cookieAttributes
param as the places to change.
…xts (#7875) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
getCookieDomain()returnsundefinedin the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)clientUat.tsto set host-only cookies (noDomainattribute) in the iframe, while the non-iframe context sets domain-scoped cookies (withDomainattribute)__client_uatentries with conflicting valuesgetCookieDomain()now falls back tohostnameinstead ofundefined, so the cookieset()either matches the non-iframe's domain-scoped cookie (common on platforms like Replit where hostname == eTLD+1) or silently fails — both preferable to creating a conflicting host-only cookieTest plan
getCookieDomainto reflect new fallback behavior__client_uatcookies are created (no host-only duplicates)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests