@gj-kit/expo-auth core concepts
Token refresh for Expo, React Native and web — a missed transient branch won’t compile.
Why this boundary exists
Section titled “Why this boundary exists”Hand-rolled refresh code classifies a 5xx, a timeout or a CORS failure as a definitive rejection and signs the user out, because the refresh result is consumed by a raw switch that TypeScript never checks for exhaustiveness — and the branch that goes missing is always the transient one. Meanwhile a Platform.OS fork drags expo-secure-store into the web bundle, two tabs race the same single-use refresh token, a boolean re-entry flag lets a 401 retry fall back into the refresh path, and an option-less re-login after signOut silently promotes a session-scoped login to durable storage on a shared machine.
Refresh is a coordination problem
Section titled “Refresh is a coordination problem”Multiple requests can discover an expired token together. Injected storage and locking let the host choose the platform mechanism while one session coordinates refresh.
A transient failure is not a sign-out
Section titled “A transient failure is not a sign-out”Closed refresh outcomes distinguish retryable service failures from invalid credentials, so a 5xx cannot silently clear a valid session.
What the package enforces
Section titled “What the package enforces”- Missing transient case won’t compile — matchRefreshOutcome takes all five endings as handler keys — omit one and the call fails to compile with “Property ‘transient’ is missing”.
- A 5xx leaves stored tokens alone — On a
transientoutcome the core writes nothing to storage, and runAuthorized rethrows your original error instead of routing the user to sign-in. - Retry twice is unrepresentable — runAuthorized requires shouldRetryAfterRefresh with no default, and the one retry it performs runs outside the refresh path — the public API has no re-entry switch.
- signIn cannot forget persistence — persistence is a required option on signIn’s second argument, so “session login → signOut → option-less re-login” cannot silently promote tokens to durable storage.
- No SecureStore in the web bundle — One
./storagesubpath forks through exports conditions, so your app writes zero Platform.OS branches and the browser graph contains no expo-secure-store.
What remains application-owned
Section titled “What remains application-owned”Treat tokens as secrets: use the supplied error contracts and never log token strings or raw authorization responses.
Continue with the two verified recipes.