fix: OIDC/SSO Token + Media issues#548
Draft
Just-Insane wants to merge 4 commits intoSableClient:devfrom
Draft
Conversation
useAsync re-threw errors after storing them in Error state. Any call site
that discards the returned Promise (e.g. fire-and-forget useEffect calls
like loadSrc() or loadThumbSrc()) produced an 'Uncaught (in promise)'
console error — notably 'Mismatched SHA-256 digest' from encrypted media
decryption failures.
The error is already fully handled: it is stored in AsyncStatus.Error state
and components surface it via a retry button. The re-throw added no value
and only caused noise / unhandled rejection warnings.
Update the test accordingly — the .catch(() => {}) guard was only needed to
silence the re-throw and is no longer necessary.
Stores refresh_token from the login response, passes it and a tokenRefreshFunction to createClient so the SDK can auto-refresh expired access tokens. The callback propagates new tokens to both the sessionsAtom (localStorage) and the service worker via pushSessionToSW, preventing 401 errors on authenticated media after token expiry.
8ecb7cc to
715e9e4
Compare
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.
ae8c97d to
c177218
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Three related bug fixes for media reliability.
1. Suppress unhandled promise rejections from
useAsyncCallbackuseAsyncCallbackstored errors inAsyncStatus.Errorstate and then re-threw them. Fire-and-forget call sites (e.g.loadSrc()/loadThumbSrc()) had no.catch(), so every failure produced an "Uncaught (in promise)" console error — most visibly "Mismatched SHA-256 digest" from encrypted media decryption. The error is already surfaced via retry UI; the re-throw added nothing. Removed it and updated the test accordingly.2. Wire OIDC token refresh through to the service worker
loginUtil.tssilently discardedrefresh_tokenfrom the login response, socreateClientwas never given atokenRefreshFunction. After an access token expired the SDK would stall and the service worker — which caches the token for authenticated media requests — would keep sending the old Bearer token, causing 401s on every media load.loginUtil.ts: capturerefresh_token/expires_in_msinto the session objectinitMatrix.ts: when arefreshTokenis present, passtokenRefreshFunctiontocreateClient; the function callsmx.refreshToken()and invokes an optionalonTokenRefreshcallbackClientRoot.tsx: supply the callback; it writes the new tokens tosessionsAtomand callspushSessionToSWso the SW picks them up immediatelyNo-op when the server doesn't return a refresh token.
3. Retry authenticated media requests on 401 in the service worker
There is a timing window between when the SDK resolves a token refresh (and updates its own in-flight requests) and when the resulting
pushSessionToSWsetSessionpostMessage is processed by the SW. Media requests that land in this window carry the stale Bearer token and receive 401. Because browsers retry failed image/video loads automatically, this produced repeated 401 bursts for the same media IDs.Added
fetchMediaWithRetry()insw.ts: on a 401 response it re-checks the in-memorysessionsmap (andpreloadedSessionfallback) for a fresher token for the same origin. If one is found, the request is retried once. Applied consistently across all four code paths in the fetch event handler. Zero overhead for non-401 responses.Fixes #
Type of change
Checklist:
AI disclosure:
Fix 1 removes
throw efromuseAsyncCallback's catch block and drops the now-unnecessary.catch(() => {})in the test. Fix 2 readsrefresh_token/expires_in_msfrom the login response and stores them in the session;buildClientspreads{ refreshToken, tokenRefreshFunction }intocreateClientoptions when a refresh token is present; theonTokenRefreshcallback inClientRootupdatessessionsAtomand callspushSessionToSWwith the new access token. Fix 3 addsfetchMediaWithRetry()tosw.ts— a thin wrapper aroundfetch()that on HTTP 401 re-checks the in-memory sessions map for a token that differs from the one just used, and if found retries the request once; covers the race window between SDK token refresh and SWsetSessionpropagation.