diff --git a/src/api/http.ts b/src/api/http.ts index c3df4fb..9206f64 100644 --- a/src/api/http.ts +++ b/src/api/http.ts @@ -80,7 +80,11 @@ export async function refreshAuthenticatedSession() { }) .catch((error) => { if (error instanceof CrossTabRefreshTimeoutError) { - throw new ApiError(error.message, 'A0401', 401) + const expiredError = new ApiError(error.message, 'A0401', 401) + clearSessionTokens() + window.dispatchEvent(new CustomEvent('cc-client-session-expired')) + redirectToLoginAfterSessionExpiry() + throw expiredError } throw error }) diff --git a/src/modules/auth/views/AuthViews.test.mjs b/src/modules/auth/views/AuthViews.test.mjs index 9939122..f635cd4 100644 --- a/src/modules/auth/views/AuthViews.test.mjs +++ b/src/modules/auth/views/AuthViews.test.mjs @@ -88,7 +88,8 @@ test('uses real auth endpoints and single-flight refresh handling', () => { assert.match(httpSource, /envelope\.code !== '200'/) assert.match(httpSource, /redirectToLoginAfterSessionExpiry/) assert.match(httpSource, /error instanceof CrossTabRefreshTimeoutError/) - assert.match(httpSource, /new ApiError\(error\.message, 'A0401', 401\)/) + assert.match(httpSource, /const expiredError = new ApiError\(error\.message, 'A0401', 401\)/) + assert.match(httpSource, /error instanceof CrossTabRefreshTimeoutError[\s\S]*window\.dispatchEvent\(new CustomEvent\('cc-client-session-expired'\)\)[\s\S]*redirectToLoginAfterSessionExpiry\(\)/) assert.match(httpSource, /window\.location\.replace\(`\/login\?redirect=\$\{encodeURIComponent\(redirect\)\}`\)/) assert.match(authStoreSource, /decodeMerchantToken/) assert.doesNotMatch(authStoreSource, /mock-client:/)