fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts - #7875

Merged
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain
Feb 18, 2026
Merged

fix(clerk-js): Prevent duplicate __client_uat cookies in iframe contexts#7875
brkalow merged 4 commits into
mainfrom
bryce/fix-iframe-client-uat-cookie-domain

Conversation

@brkalow

@brkalowbrkalow commented Feb 18, 2026

Copy link
Copy Markdown
Member

Summary

  • When an app runs in both an iframe and a standalone tab, getCookieDomain() returns undefined in the iframe because the eTLD+1 test cookie probe fails (third-party cookie restrictions prevent domain cookies from sticking)
  • This causes clientUat.ts to set host-only cookies (no Domain attribute) in the iframe, while the non-iframe context sets domain-scoped cookies (with Domain attribute)
  • The browser treats these as two separate cookies, creating duplicate __client_uat entries with conflicting values
  • Fix: getCookieDomain() now falls back to hostname instead of undefined, so the cookie set() 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 cookie

Test plan

  • Updated existing unit test for getCookieDomain to reflect new fallback behavior
  • Manual testing: load app in iframe on Replit, verify only domain-scoped __client_uat cookies are created (no host-only duplicates)
  • Manual testing: load app in standalone tab, verify cookie behavior unchanged

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed __client_uat cookie domain handling so it behaves correctly when the app loads in iframe and non-iframe contexts.
    • Added a deterministic fallback for cookie domain resolution (returns the hostname when a broader domain can't be derived) to avoid host-only cookie conflicts in restricted environments.
    • Ensured cookie SameSite and Secure attributes are consistently applied during domain probing.
  • Tests

    • Added/updated tests to verify attribute forwarding to the domain probe and the hostname fallback.

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-bot

changeset-botBot commented Feb 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b245c99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

@vercel

vercelBot commented Feb 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentFeb 18, 2026 7:36pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Feb 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7875

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7875

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7875

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7875

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7875

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7875

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7875

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7875

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7875

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7875

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@7875

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7875

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7875

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7875

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7875

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7875

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7875

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7875

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7875

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7875

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7875

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7875

commit: b245c99

@coderabbitai

coderabbitaiBot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds 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)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title accurately summarizes the main change: preventing duplicate __client_uat cookies in iframe contexts, which is the core problem being addressed by the PR.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread.changeset/fix-iframe-client-uat-cookie-domain.md Outdated
// 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +11 to +23
/**
* @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 },
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/clerk-js/src/core/auth/getCookieDomain.ts

Repository: 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.

Suggested change
/**
* @paramhostname-ThehostnametodeterminetheeTLD+1for.
* @paramcookieHandler-ThecookiehandlertousefortheeTLD+1probe.
* @paramcookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*/
exportfunctiongetCookieDomain(
hostname=window.location.hostname,
cookieHandler=eTLDCookie,
cookieAttributes?: {sameSite?: string; secure?: boolean},
){
/**
*`@param`hostname-ThehostnametodeterminetheeTLD+1for.
*`@param`cookieHandler-ThecookiehandlertousefortheeTLD+1probe.
*`@param`cookieAttributes-Optionalcookieattributes(sameSite,secure)touse
*duringtheeTLD+1probe.Theseshouldmatchtheattributesthatwillbeused
*whensettingtheactualcookie,sotheprobeaccuratelyreflectswhethera
*domain-scopedcookiecanbesetinthecurrentcontext.
*`@returns`TheresolvedeTLD+1domaintouseforsettingcookies.
*`@throws`Ifthecookiehandlerthrowswhileprobingdomains.
*`@example`
*getCookieDomain('app.example.com',undefined,{sameSite: 'None',secure: true});
*/
exportfunctiongetCookieDomain(
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.

@brkalow
brkalow merged commit 78dc225 into mainFeb 18, 2026
65 of 66 checks passed
@brkalow
brkalow deleted the bryce/fix-iframe-client-uat-cookie-domain branch February 18, 2026 20:55
brkalow added a commit that referenced this pull request Feb 18, 2026
…xts (#7875)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@brkalow@jacekradko