Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
55e8a21
docs: add Stack Auth → Hexclave rebrand plan
BilalG1 May 21, 2026
0bef15f
docs: cover query params, storage keys, custom events, and dev tool i…
BilalG1 May 22, 2026
1843a37
docs: v7 — internal-only renames, env var dual-read, 3-PR rollout
BilalG1 May 22, 2026
a1d043b
docs: fold PR 1 discovery findings into the plan
BilalG1 May 22, 2026
8b7ccfc
docs: correct x-stack-auth legacy header — SDK-internal, not backend
BilalG1 May 22, 2026
e60550a
feat(hexclave): add Hexclave* SDK export aliases
BilalG1 May 22, 2026
fc781de
feat(hexclave): JWT validator accepts both stack-auth.com and hexclav…
BilalG1 May 22, 2026
2a056ea
feat(hexclave): dual-accept x-hexclave-* request headers
BilalG1 May 22, 2026
30ffd60
feat(hexclave): register ask_hexclave MCP tool alongside ask_stack_auth
BilalG1 May 22, 2026
7fed864
feat(hexclave): env vars, cookies, bearer, symbols, query params, int…
BilalG1 May 22, 2026
32131ea
feat(hexclave): rename dev-tool DOM identifiers and switch its header…
BilalG1 May 22, 2026
21217fb
fix(hexclave): review-pass fixes for PR 1
BilalG1 May 22, 2026
4b16cc5
test(hexclave): hide x-hexclave-request-id in snapshots; regen projec…
BilalG1 May 22, 2026
58199d7
feat(hexclave): regenerate OpenAPI docs for hexclave_response_mode qu…
BilalG1 May 22, 2026
6d07139
fix(hexclave): address review comment + hide duplicate hexclave heade…
BilalG1 May 22, 2026
5e5189f
fix(hexclave): SDK fallback tests — restore singular helpers, context…
BilalG1 May 22, 2026
f911217
fix(hexclave): address review comments + auth-like test helper Bearer…
BilalG1 May 22, 2026
4977c73
chore(hexclave): temporarily bump docker workflow sleep to diagnose d…
BilalG1 May 22, 2026
8a0d6f4
fix(hexclave): optimize entrypoint sentinel-replace to only sed match…
BilalG1 May 23, 2026
8ab3789
fix(docker): guard sentinel-replace against bare STACK_ENV_VAR_SENTIN…
May 23, 2026
afc8ff8
fix(lint): correct ternary indentation in parseAuthorizationHeaderValue
May 23, 2026
3d36caf
fix(hexclave): preserve stackauth_ Bearer emission; mirror URL envs i…
May 23, 2026
3f51ed5
fix(hexclave): update e2e tests for cookie dual-write + storage-key r…
BilalG1 May 23, 2026
d111c60
fix(hexclave): three more test-side follow-ups for cookie/header dual…
BilalG1 May 23, 2026
b60a455
fix(hexclave): address pr review compatibility feedback
BilalG1 May 23, 2026
dce66a8
fix(hexclave): drop duplicate Hexclave*App type re-exports
BilalG1 May 23, 2026
47e7c6e
fix(hexclave): align e2e tests with hexclave-only emit (cookies, cros…
BilalG1 May 23, 2026
3a480a3
Merge remote-tracking branch 'origin/dev' into cl/hexclave-pr1
BilalG1 May 23, 2026
5ad6888
fix(tests): align with HexclaveAssertionError suffix + hexclave_cross…
BilalG1 May 23, 2026
809ca8d
Merge branch 'dev' into cl/hexclave-pr1
BilalG1 May 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(hexclave): SDK fallback tests — restore singular helpers, context…
…-aware default delete, dual-write coverage

Four root causes of the E2E Fallback Tests (Node 22.x) failures in PR #1475:

1. auth-like.test.ts: 4 tests asserted /^Bearer\s+stackauth_.+/, but the SDK now
   emits the new hexclave_ prefix. Regex updated to accept either prefix.
2. cookies.test.ts 'roundtrip ... encode/decode' (line 319): called the singular
   clientApp._getCustomRefreshCookieName(domain), which the cookie agent had
   removed as 'orphaned' (the check missed e2e callers). Restored the singular
   _getCustomRefreshCookieName and _getRefreshTokenDefaultCookieNameForSecure
   as back-compat wrappers returning the legacy stack-* name.
3. cookies.test.ts 'should set refresh token cookies for trusted parent domains'
   (line 184): defaultCookieName was never deleted because the default-cookie
   cleanup in _queueCustomRefreshCookieUpdate used the server-side
   setOrDeleteCookie unconditionally, even on the browser context where the
   surrounding code uses setOrDeleteCookieClient. Made the cleanup
   context-aware to match the setCookie helper above it.
4. cookies.test.ts 'should eagerly create cross-subdomain cookie on construction'
   (line 424): _ensureCrossSubdomainCookieExists skipped writing if ANY custom
   cookie name was present. With dual-write, deleting just one (as the test
   does) left the other and skipped the eager recreation. Changed .some() to
   .every() so a missing legacy-name cookie still triggers the dual-write.
  • Loading branch information
BilalG1 committed May 22, 2026
commit 5e5189fe7f12e51c064b58858db3fcf7b3fad647
8 changes: 4 additions & 4 deletions apps/e2e/tests/js/auth-like.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

const credential = bearerMatch[1];
if (!credential.startsWith(STACK_AUTHORIZATION_VALUE_PREFIX)) {
throw new Error(`Invalid stackauth authorization credential: ${credential}`);

Check failure on line 26 in apps/e2e/tests/js/auth-like.test.ts

View workflow job for this annotation

GitHub Actions / E2E Fallback Tests (Node 22.x)

tests/js/auth-like.test.ts > getUser should work with Authorization header in request-like tokenStore

Error: Invalid stackauth authorization credential: hexclave_eyJhY2Nlc3NUb2tlbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJazl4YzFacldFMVVNbFl0UlNKOS5leUp6ZFdJaU9pSTVZV1EwT1RsaFpDMWxOekU1TFRRellqY3RPVFV6WWkwMk5UTTNOelUzWldRMFpqa2lMQ0p3Y205cVpXTjBYMmxrSWpvaVpUSmhaamN4TkRndE1UVmtOeTAwTkRFeExUa3lObVV0TmprellXVTJZMkkzWkdaa0lpd2lZbkpoYm1Ob1gybGtJam9pYldGcGJpSXNJbkpsWm5KbGMyaGZkRzlyWlc1ZmFXUWlPaUl6TUdNNU9HRTNOUzB4TnpOaExUUTFNakl0T1dGaE5pMWtabU00WlROaVltVTJNalFpTENKeWIyeGxJam9pWVhWMGFHVnVkR2xqWVhSbFpDSXNJbTVoYldVaU9tNTFiR3dzSW1WdFlXbHNJam9pZEdWemRFQjBaWE4wTG1OdmJTSXNJbVZ0WVdsc1gzWmxjbWxtYVdWa0lqcG1ZV3h6WlN3aWMyVnNaV04wWldSZmRHVmhiVjlwWkNJNmJuVnNiQ3dpYzJsbmJtVmtYM1Z3WDJGMElqb3hOemM1TkRreU9URTFMQ0pwYzE5aGJtOXVlVzF2ZFhNaU9tWmhiSE5sTENKcGMxOXlaWE4wY21samRHVmtJanBtWVd4elpTd2ljbVZ6ZEhKcFkzUmxaRjl5WldGemIyNGlPbTUxYkd3c0luSmxjWFZwY21WelgzUnZkSEJmYldaaElqcG1ZV3h6WlN3aWFYTnpJam9pYUhSMGNEb3ZMMnh2WTJGc2FHOXpkRG80TVRBeUwyRndhUzkyTVM5d2NtOXFaV04wY3k5bE1tRm1OekUwT0MweE5XUTNMVFEwTVRFdE9USTJaUzAyT1ROaFpUWmpZamRrWm1RaUxDSnBZWFFpT2pFM056azBPVEk1TVRZc0ltRjFaQ0k2SW1VeVlXWTNNVFE0TFRFMVpEY3RORFF4TVMwNU1qWmxMVFk1TTJGbE5tTmlOMlJtWkNJc0ltVjRjQ0k2TVRjM09UUTVNamszTm4wLjRoYkZVek9Tb3RaQWF6VU9VZmR4dHQxYXlfOXEwc2RzRk9xa2R6eThVSDlhcVlXay00VzFhSV9WQ29jRjkyM0MzWFRtOVFsa2dYdjk5UWJTOHRoc3RBIiwicmVmcmVzaFRva2VuIjoic3FxbmduOGFjbjg4YjF2ODBueHd6eHQ5YTZrbjltMWU2d2NtZGpkOGc4ejByIn0= ❯ parseAuthorizationHeaderValue tests/js/auth-like.test.ts:26:11 ❯ tests/js/auth-like.test.ts:591:10

Check failure on line 26 in apps/e2e/tests/js/auth-like.test.ts

View workflow job for this annotation

GitHub Actions / E2E Fallback Tests (Node 22.x)

tests/js/auth-like.test.ts > clientApp.getAuthorizationHeader should work with tokenStore option

Error: Invalid stackauth authorization credential: hexclave_eyJhY2Nlc3NUb2tlbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJa2R5VUdkaFZVbHZTME5LZWlKOS5leUp6ZFdJaU9pSTJNakU1TUdSaFpDMDFaV1UxTFRSaE56VXRPVGsxWVMwNU5EaGxPVGMzTkdaaFpUY2lMQ0p3Y205cVpXTjBYMmxrSWpvaU5ERXdZalUzTlRVdFltRTBPQzAwTURCaExUZ3pNVFV0TnpRMVpqYzRNalZsTVRVNUlpd2lZbkpoYm1Ob1gybGtJam9pYldGcGJpSXNJbkpsWm5KbGMyaGZkRzlyWlc1ZmFXUWlPaUkyWW1ZeU9HSmhNeTB5WWpJMUxUUTROekV0WVRBeVpTMHdNREkyTmpreU1qWTVZbVFpTENKeWIyeGxJam9pWVhWMGFHVnVkR2xqWVhSbFpDSXNJbTVoYldVaU9tNTFiR3dzSW1WdFlXbHNJam9pZEdWemRFQjBaWE4wTG1OdmJTSXNJbVZ0WVdsc1gzWmxjbWxtYVdWa0lqcG1ZV3h6WlN3aWMyVnNaV04wWldSZmRHVmhiVjlwWkNJNmJuVnNiQ3dpYzJsbmJtVmtYM1Z3WDJGMElqb3hOemM1TkRreU9EZzVMQ0pwYzE5aGJtOXVlVzF2ZFhNaU9tWmhiSE5sTENKcGMxOXlaWE4wY21samRHVmtJanBtWVd4elpTd2ljbVZ6ZEhKcFkzUmxaRjl5WldGemIyNGlPbTUxYkd3c0luSmxjWFZwY21WelgzUnZkSEJmYldaaElqcG1ZV3h6WlN3aWFYTnpJam9pYUhSMGNEb3ZMMnh2WTJGc2FHOXpkRG80TVRBeUwyRndhUzkyTVM5d2NtOXFaV04wY3k4ME1UQmlOVGMxTlMxaVlUUTRMVFF3TUdFdE9ETXhOUzAzTkRWbU56Z3lOV1V4TlRraUxDSnBZWFFpT2pFM056azBPVEk0T0Rrc0ltRjFaQ0k2SWpReE1HSTFOelUxTFdKaE5EZ3ROREF3WVMwNE16RTFMVGMwTldZM09ESTFaVEUxT1NJc0ltVjRjQ0k2TVRjM09UUTVNamswT1gwLl84UVZBSDJhb0o0MjVYaFpndC1yd2YwZzhqT2RoUVU1c2Z4SnlpcFZ3VXZHenpacVZCeXpta0hsemtNek5Hb215M3N3OGpJclR4RmE0cHBtT2JKelBRIiwicmVmcmVzaFRva2VuIjoiZWtzMGZ4YjY0cWM2bnk0YXpnZTZkenM3Nm5qemtuMHhkNXY1djN2NTlxODZyIn0= ❯ parseAuthorizationHeaderValue tests/js/auth-like.test.ts:26:11 ❯ tests/js/auth-like.test.ts:395:10

Check failure on line 26 in apps/e2e/tests/js/auth-like.test.ts

View workflow job for this annotation

GitHub Actions / E2E Fallback Tests (Node 22.x)

tests/js/auth-like.test.ts > clientApp.getAuthorizationHeader should return Bearer header value

Error: Invalid stackauth authorization credential: hexclave_eyJhY2Nlc3NUb2tlbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJakUzTlc5YVpXTnlTR3h6TVNKOS5leUp6ZFdJaU9pSmlOV1psTlRBeVppMDJZVEZtTFRSbE5XSXRPV1l5WkMweE5qVmxOek0yWW1Gak1XTWlMQ0p3Y205cVpXTjBYMmxrSWpvaU5HUTVOekV4WkRBdE0yVXdPQzAwWWpaaExUZzJOR1l0TW1JNE1qbGpPV1E0T0dRMUlpd2lZbkpoYm1Ob1gybGtJam9pYldGcGJpSXNJbkpsWm5KbGMyaGZkRzlyWlc1ZmFXUWlPaUl5TlRBMk5qRTVZeTA0TlRZMUxUUTVZbVF0WVRSbU1pMDNNVGRqTWpObVl6UXhabUVpTENKeWIyeGxJam9pWVhWMGFHVnVkR2xqWVhSbFpDSXNJbTVoYldVaU9tNTFiR3dzSW1WdFlXbHNJam9pZEdWemRFQjBaWE4wTG1OdmJTSXNJbVZ0WVdsc1gzWmxjbWxtYVdWa0lqcG1ZV3h6WlN3aWMyVnNaV04wWldSZmRHVmhiVjlwWkNJNmJuVnNiQ3dpYzJsbmJtVmtYM1Z3WDJGMElqb3hOemM1TkRreU9EYzRMQ0pwYzE5aGJtOXVlVzF2ZFhNaU9tWmhiSE5sTENKcGMxOXlaWE4wY21samRHVmtJanBtWVd4elpTd2ljbVZ6ZEhKcFkzUmxaRjl5WldGemIyNGlPbTUxYkd3c0luSmxjWFZwY21WelgzUnZkSEJmYldaaElqcG1ZV3h6WlN3aWFYTnpJam9pYUhSMGNEb3ZMMnh2WTJGc2FHOXpkRG80TVRBeUwyRndhUzkyTVM5d2NtOXFaV04wY3k4MFpEazNNVEZrTUMwelpUQTRMVFJpTm1FdE9EWTBaaTB5WWpneU9XTTVaRGc0WkRVaUxDSnBZWFFpT2pFM056azBPVEk0T0RBc0ltRjFaQ0k2SWpSa09UY3hNV1F3TFRObE1EZ3ROR0kyWVMwNE5qUm1MVEppT0RJNVl6bGtPRGhrTlNJc0ltVjRjQ0k2TVRjM09UUTVNamswTUgwLnd0bmM0aWVGRE83ZHFIZXBCdHBJay1COGdrb0tLTTkyUDVvb3laVm9zN0dyeXZPOW9ackpmOS1QUnVhLU9MOTB2Q21mNFhxUVlnenNvbWFOeFlwaXdBIiwicmVmcmVzaFRva2VuIjoiYnZ4ZHRkMTNtOTJiZHZ4dmFycTIwc2hkczh5eHJuY2M3eWoyajZzZGMzaHlyIn0= ❯ parseAuthorizationHeaderValue tests/js/auth-like.test.ts:26:11 ❯ tests/js/auth-like.test.ts:376:10

Check failure on line 26 in apps/e2e/tests/js/auth-like.test.ts

View workflow job for this annotation

GitHub Actions / E2E Fallback Tests (Node 22.x)

tests/js/auth-like.test.ts > getAuthorizationHeader should return a Bearer token that works for authentication

Error: Invalid stackauth authorization credential: hexclave_eyJhY2Nlc3NUb2tlbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJazloVW1Kb1pqYzVNems0YmlKOS5leUp6ZFdJaU9pSXhNamd4TURRMk1TMDRNVEkzTFRReVpXWXRPVGhoT1MxalpEZGxOR016TUdRMFpEWWlMQ0p3Y205cVpXTjBYMmxrSWpvaVkyRXhNR0ZoTmprdE5EaGxOQzAwTURVNUxXRXlOR0V0TjJFeFpUQTBaamt6WlRkbElpd2lZbkpoYm1Ob1gybGtJam9pYldGcGJpSXNJbkpsWm5KbGMyaGZkRzlyWlc1ZmFXUWlPaUl3WlROaU5tTTVPUzB4WXpRMExUUTNZbVl0WW1NeU9DMWhZVFkwTW1WaU5ESTBOVGdpTENKeWIyeGxJam9pWVhWMGFHVnVkR2xqWVhSbFpDSXNJbTVoYldVaU9tNTFiR3dzSW1WdFlXbHNJam9pZEdWemRFQjBaWE4wTG1OdmJTSXNJbVZ0WVdsc1gzWmxjbWxtYVdWa0lqcG1ZV3h6WlN3aWMyVnNaV04wWldSZmRHVmhiVjlwWkNJNmJuVnNiQ3dpYzJsbmJtVmtYM1Z3WDJGMElqb3hOemM1TkRreU9EUTRMQ0pwYzE5aGJtOXVlVzF2ZFhNaU9tWmhiSE5sTENKcGMxOXlaWE4wY21samRHVmtJanBtWVd4elpTd2ljbVZ6ZEhKcFkzUmxaRjl5WldGemIyNGlPbTUxYkd3c0luSmxjWFZwY21WelgzUnZkSEJmYldaaElqcG1ZV3h6WlN3aWFYTnpJam9pYUhSMGNEb3ZMMnh2WTJGc2FHOXpkRG80TVRBeUwyRndhUzkyTVM5d2NtOXFaV04wY3k5allURXdZV0UyT1MwME9HVTBMVFF3TlRrdFlUSTBZUzAzWVRGbE1EUm1PVE5sTjJVaUxDSnBZWFFpT2pFM056azBPVEk0TkRrc0ltRjFaQ0k2SW1OaE1UQmhZVFk1TFRRNFpUUXROREExT1MxaE1qUmhMVGRoTVdVd05HWTVNMlUzWlNJc0ltVjRjQ0k2TVRjM09UUTVNamt3T1gwLmVCUWFzY21aQW1CWk5lR1VBclptTlJRSlNzWHBEVFRZUDlOazF5dE5KZkx1ME1xV29QN0ZvT3RqSTlHQkZ5THZfanJOR0NNUEtDaEdqQmJhMW5ZaUZBIiwicmVmcmVzaFRva2VuIjoiZGptc21landxanhnenkzeGh6bjVhMGF4NXgwMTlhNndiaHRrbWNuc2dqeXE4In0= ❯ parseAuthorizationHeaderValue tests/js/auth-like.test.ts:26:11 ❯ tests/js/auth-like.test.ts:281:37
}

const encodedAuthJson = credential.slice(STACK_AUTHORIZATION_VALUE_PREFIX.length);
Expand Down Expand Up @@ -277,7 +277,7 @@
if (authorizationHeader == null) {
throw new Error("Expected authorization header for signed-in user.");
}
expect(authorizationHeader).toMatch(/^Bearer\s+stackauth_.+/);
expect(authorizationHeader).toMatch(/^Bearer\s+(stackauth_|hexclave_).+/);
const parsedAuthorizationHeader = parseAuthorizationHeaderValue(authorizationHeader);
const authJson = await clientApp.getAuthJson();
expect(parsedAuthorizationHeader).toEqual(authJson);
Expand Down Expand Up @@ -372,7 +372,7 @@
if (authorizationHeader == null) {
throw new Error("Expected authorization header for signed-in user.");
}
expect(authorizationHeader).toMatch(/^Bearer\s+stackauth_.+/);
expect(authorizationHeader).toMatch(/^Bearer\s+(stackauth_|hexclave_).+/);
expect(parseAuthorizationHeaderValue(authorizationHeader)).toEqual(await clientApp.getAuthJson());
});

Expand All @@ -391,7 +391,7 @@
if (authorizationHeader == null) {
throw new Error("Expected authorization header for signed-in user.");
}
expect(authorizationHeader).toMatch(/^Bearer\s+stackauth_.+/);
expect(authorizationHeader).toMatch(/^Bearer\s+(stackauth_|hexclave_).+/);
expect(parseAuthorizationHeaderValue(authorizationHeader)).toEqual(await clientApp.getAuthJson({ tokenStore: "memory" }));
});

Expand Down Expand Up @@ -587,7 +587,7 @@
if (authorizationHeader == null) {
throw new Error("Expected authorization header for signed-in user.");
}
expect(authorizationHeader).toMatch(/^Bearer\s+stackauth_.+/);
expect(authorizationHeader).toMatch(/^Bearer\s+(stackauth_|hexclave_).+/);
expect(parseAuthorizationHeaderValue(authorizationHeader)).toEqual(await clientApp.getAuthJson());

const requestLike = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,16 @@ export class _StackClientAppImplIncomplete<HasTokenStore extends boolean, Projec
}
return null;
}
// Hexclave rebrand: back-compat singular helpers. The plural variants above are the canonical
// path (they return both bases for dual-write); these return only the legacy stack-* name so
// integration tests and any older callers that look up a single name keep resolving.
private _getCustomRefreshCookieName(domain: string): string {
const encoded = encodeBase32(new TextEncoder().encode(domain.toLowerCase()));
return `${this._refreshTokenCookieName}--custom-${encoded}`;
}
private _getRefreshTokenDefaultCookieNameForSecure(secure: boolean): string {
return `${secure ? "__Host-" : ""}${this._refreshTokenCookieName}--default`;
}
private _formatRefreshCookieValue(refreshToken: string, updatedAt: number): string {
return JSON.stringify({
refresh_token: refreshToken,
Expand Down Expand Up @@ -934,9 +944,11 @@ export class _StackClientAppImplIncomplete<HasTokenStore extends boolean, Projec
return;
}
const cookies = this._getAllBrowserCookies();
// Hexclave rebrand: dual-write the custom cross-subdomain cookie under both names.
// Hexclave rebrand: dual-write the custom cross-subdomain cookie under both names. Only
// skip if BOTH names already exist — if either is missing (e.g. one was manually cleared),
// re-write the pair so the legacy-name read path stays valid.
const customCookieNames = this._getCustomRefreshCookieNames(domain.data);
if (customCookieNames.some((name) => cookies[name])) {
if (customCookieNames.every((name) => cookies[name])) {
return;
}
const { refreshToken, updatedAt } = this._extractRefreshTokenFromCookieMap(cookies);
Expand Down Expand Up @@ -984,9 +996,15 @@ export class _StackClientAppImplIncomplete<HasTokenStore extends boolean, Projec
const value = refreshToken && updatedAt ? this._formatRefreshCookieValue(refreshToken, updatedAt) : null;
await setCookie(domain.data, value);
const isSecure = await isSecureCookieContext();
// Hexclave rebrand: delete the (now-superseded) default cookie under both names.
// Hexclave rebrand: delete the (now-superseded) default cookie under both names. Match the
// browser/server split used by setCookie above — otherwise the browser-side onChange path
// ends up calling the Next.js server-cookie helper and the default cookie is never cleared.
for (const defaultName of this._getRefreshTokenDefaultCookieNamesForSecure(isSecure)) {
await setOrDeleteCookie(defaultName, null, cookieOptions);
if (context === "browser") {
setOrDeleteCookieClient(defaultName, null, cookieOptions);
} else {
await setOrDeleteCookie(defaultName, null, cookieOptions);
}
}
});
}
Expand Down
Loading