Skip to content

Commit c177218

Browse files
committed
fix: retry media requests once on 401 with refreshed session
There is a timing window between when the SDK refreshes its access token (tokenRefreshFunction resolves and pushSessionToSW is called) and when the resulting setSession postMessage is processed by the SW. Media requests that land in this window carry the stale token and receive 401. The browser then retries those image/video loads, hitting the SW again with the same stale token — producing the repeated 401 bursts visible in the console. fetchMediaWithRetry() resolves this by retrying once on 401: it re-checks the in-memory sessions map (and preloadedSession fallback) for a different access token. By the time the retry runs, setSession will normally have been processed and the map will hold the new token. Applied consistently across all four branches of the fetch handler.
1 parent 715e9e4 commit c177218

1 file changed

Lines changed: 36 additions & 5 deletions

File tree

src/sw.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,37 @@ function fetchConfig(token: string): RequestInit {
579579
};
580580
}
581581

582+
/**
583+
* Fetch a media URL, retrying once with the most-current in-memory session on 401.
584+
*
585+
* There is a timing window between when the SDK refreshes its access token
586+
* (tokenRefreshFunction resolves) and when the resulting pushSessionToSW()
587+
* postMessage is processed by the SW. Media requests that land in this window
588+
* are sent with the stale token and receive 401. By the time the retry runs,
589+
* the setSession message will normally have been processed and sessions will
590+
* hold the new token.
591+
*/
592+
async function fetchMediaWithRetry(
593+
url: string,
594+
token: string,
595+
redirect: RequestRedirect,
596+
clientId: string
597+
): Promise<Response> {
598+
const response = await fetch(url, { ...fetchConfig(token), redirect });
599+
if (response.status !== 401) return response;
600+
601+
const updated =
602+
(clientId ? sessions.get(clientId) : undefined) ??
603+
[...sessions.values()].find((s) => validMediaRequest(url, s.baseUrl)) ??
604+
preloadedSession;
605+
606+
if (updated && updated.accessToken !== token && validMediaRequest(url, updated.baseUrl)) {
607+
return fetch(url, { ...fetchConfig(updated.accessToken), redirect });
608+
}
609+
610+
return response;
611+
}
612+
582613
self.addEventListener('message', (event: ExtendableMessageEvent) => {
583614
if (event.data.type === 'togglePush') {
584615
const token = event.data?.token;
@@ -609,7 +640,7 @@ self.addEventListener('fetch', (event: FetchEvent) => {
609640

610641
const session = clientId ? sessions.get(clientId) : undefined;
611642
if (session && validMediaRequest(url, session.baseUrl)) {
612-
event.respondWith(fetch(url, { ...fetchConfig(session.accessToken), redirect }));
643+
event.respondWith(fetchMediaWithRetry(url, session.accessToken, redirect, clientId));
613644
return;
614645
}
615646

@@ -629,7 +660,7 @@ self.addEventListener('fetch', (event: FetchEvent) => {
629660
? preloadedSession
630661
: undefined);
631662
if (byBaseUrl) {
632-
event.respondWith(fetch(url, { ...fetchConfig(byBaseUrl.accessToken), redirect }));
663+
event.respondWith(fetchMediaWithRetry(url, byBaseUrl.accessToken, redirect, clientId));
633664
return;
634665
}
635666

@@ -639,7 +670,7 @@ self.addEventListener('fetch', (event: FetchEvent) => {
639670
event.respondWith(
640671
loadPersistedSession().then((persisted) => {
641672
if (persisted && validMediaRequest(url, persisted.baseUrl)) {
642-
return fetch(url, { ...fetchConfig(persisted.accessToken), redirect });
673+
return fetchMediaWithRetry(url, persisted.accessToken, redirect, '');
643674
}
644675
return fetch(event.request);
645676
})
@@ -651,13 +682,13 @@ self.addEventListener('fetch', (event: FetchEvent) => {
651682
requestSessionWithTimeout(clientId).then(async (s) => {
652683
// Primary: session received from the live client window.
653684
if (s && validMediaRequest(url, s.baseUrl)) {
654-
return fetch(url, { ...fetchConfig(s.accessToken), redirect });
685+
return fetchMediaWithRetry(url, s.accessToken, redirect, clientId);
655686
}
656687
// Fallback: try the persisted session (helps when SW restarts on iOS and
657688
// the client window hasn't responded to requestSession yet).
658689
const persisted = await loadPersistedSession();
659690
if (persisted && validMediaRequest(url, persisted.baseUrl)) {
660-
return fetch(url, { ...fetchConfig(persisted.accessToken), redirect });
691+
return fetchMediaWithRetry(url, persisted.accessToken, redirect, clientId);
661692
}
662693
console.warn(
663694
'[SW fetch] No valid session for media request',

0 commit comments

Comments
 (0)