From 6c3725885ce4289812389b0280efd5c8321b9732 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 18:54:30 +0000 Subject: [PATCH 1/2] fix(dashboard): use primary hexclave-refresh- cookie name for impersonation The impersonation snippet was setting a stack-refresh- (legacy) cookie, but the SDK reads hexclave-refresh- cookies first. On production HTTPS sites, not all deployed SDK versions fall back to the legacy structured prefix, causing impersonation to silently fail. Changes: - Set cookie under hexclave-refresh-{pid}--default (primary name) - Clear both hexclave- and stack- cookie variants before setting - Extract pid variable to reduce repetition Co-Authored-By: mantra --- apps/dashboard/src/components/user-dialogs.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/components/user-dialogs.tsx b/apps/dashboard/src/components/user-dialogs.tsx index 66cdcaa3d..9ab9aafa8 100644 --- a/apps/dashboard/src/components/user-dialogs.tsx +++ b/apps/dashboard/src/components/user-dialogs.tsx @@ -36,12 +36,21 @@ export function generateImpersonateSnippet( refreshToken: string, expiresAtDate: Date, ): string { + // Clear ALL cookie variants (both hexclave- and legacy stack- prefixes, with + // and without __Host-) so no stale token takes precedence over the one we set. + // Set the cookie under the primary hexclave-refresh- name the SDK reads first; + // using the legacy stack-refresh- name caused impersonation to silently fail on + // production because not all deployed SDK versions fall back to it. + const pid = encodeURIComponent(projectId); return deindent` document.cookie = 'hexclave-access=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; document.cookie = 'stack-access=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; - document.cookie = 'hexclave-refresh-${encodeURIComponent(projectId)}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; - document.cookie = '__Host-hexclave-refresh-${encodeURIComponent(projectId)}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); - document.cookie = (location.protocol === 'https:' ? '__Host-' : '') + 'stack-refresh-${encodeURIComponent(projectId)}--default=' + encodeURIComponent(JSON.stringify({ refresh_token: ${JSON.stringify(refreshToken)}, updated_at_millis: Date.now() })) + '; expires=${expiresAtDate.toUTCString()}; path=/' + (location.protocol === 'https:' ? '; secure' : ''); + document.cookie = 'hexclave-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + document.cookie = '__Host-hexclave-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); + document.cookie = 'stack-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + document.cookie = '__Host-stack-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); + document.cookie = 'stack-refresh-${pid}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + document.cookie = (location.protocol === 'https:' ? '__Host-' : '') + 'hexclave-refresh-${pid}--default=' + encodeURIComponent(JSON.stringify({ refresh_token: ${JSON.stringify(refreshToken)}, updated_at_millis: Date.now() })) + '; expires=${expiresAtDate.toUTCString()}; path=/' + (location.protocol === 'https:' ? '; secure' : ''); window.location.reload(); `; } From 6177194f23603ddabb1b595db4b0d6b4fe901e8f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:16:56 +0000 Subject: [PATCH 2/2] fix(dashboard): dynamically expire all refresh cookie variants in impersonation snippet Co-Authored-By: mantra --- .../dashboard/src/components/user-dialogs.tsx | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/dashboard/src/components/user-dialogs.tsx b/apps/dashboard/src/components/user-dialogs.tsx index 9ab9aafa8..e066ceb1b 100644 --- a/apps/dashboard/src/components/user-dialogs.tsx +++ b/apps/dashboard/src/components/user-dialogs.tsx @@ -36,20 +36,34 @@ export function generateImpersonateSnippet( refreshToken: string, expiresAtDate: Date, ): string { - // Clear ALL cookie variants (both hexclave- and legacy stack- prefixes, with - // and without __Host-) so no stale token takes precedence over the one we set. - // Set the cookie under the primary hexclave-refresh- name the SDK reads first; - // using the legacy stack-refresh- name caused impersonation to silently fail on - // production because not all deployed SDK versions fall back to it. + // Dynamically expire EVERY refresh cookie for this project before setting ours. + // The SDK selects the refresh token by scanning all cookies whose name starts + // with `hexclave-refresh-{pid}--` / `stack-refresh-{pid}--` (with or without the + // __Host- prefix) and keeping the one with the newest `updated_at_millis`. A + // hardcoded list of names misses structured variants like the cross-subdomain + // `--custom-*` cookies, so a stale/future-dated one could still win the selection + // and keep impersonation from taking effect. We instead iterate document.cookie + // and delete any name matching the two bases (bare legacy, `--*` structured, and + // __Host- prefixed) so nothing but the cookie we set below survives. + // We set the new token under the primary hexclave-refresh- name the SDK reads + // first; using the legacy stack-refresh- name caused impersonation to silently + // fail on production because not all deployed SDK versions fall back to it. const pid = encodeURIComponent(projectId); return deindent` document.cookie = 'hexclave-access=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; document.cookie = 'stack-access=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; - document.cookie = 'hexclave-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; - document.cookie = '__Host-hexclave-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); - document.cookie = 'stack-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; - document.cookie = '__Host-stack-refresh-${pid}--default=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); - document.cookie = 'stack-refresh-${pid}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + var impersonationRefreshBases = ['hexclave-refresh-${pid}', 'stack-refresh-${pid}']; + document.cookie.split(';').forEach(function (rawCookie) { + var cookieName = rawCookie.split('=')[0].trim(); + if (!cookieName) return; + var bareName = cookieName.replace(/^__Host-/, ''); + var matchesBase = impersonationRefreshBases.some(function (base) { + return bareName === base || bareName.indexOf(base + '--') === 0; + }); + if (matchesBase) { + document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + (location.protocol === 'https:' ? '; secure' : ''); + } + }); document.cookie = (location.protocol === 'https:' ? '__Host-' : '') + 'hexclave-refresh-${pid}--default=' + encodeURIComponent(JSON.stringify({ refresh_token: ${JSON.stringify(refreshToken)}, updated_at_millis: Date.now() })) + '; expires=${expiresAtDate.toUTCString()}; path=/' + (location.protocol === 'https:' ? '; secure' : ''); window.location.reload(); `;