Summary
In lib/KindeAuthProvider.tsx, the authenticate() helper races request.promptAsync(...) against a timeout when authTimeoutMs is provided (used by the new silent switchOrg flow with prompt: none). When the timeout wins the race, the underlying AuthRequest.promptAsync(...) call is never cancelled or dismissed — the in-app browser/auth session opened for the silent attempt keeps running in the background.
switchOrg then falls back to an interactive prompt: login attempt, which opens a second AuthRequest/browser session on top of the still-running first one. This is very likely the root cause of the "stuck browser" issue referenced in PR #134's description, where chained org switches can get stuck because Expo browsers opened for silent reauth aren't handled/closed correctly.
Affected area
lib/KindeAuthProvider.tsx: the authenticate() helper (silent-auth timeout race added for switchOrg), and the switchOrg() method that performs the silent-then-interactive fallback.
Required changes
- When the
authTimeoutMs timeout fires (i.e., the timeout branch of the Promise.race wins), explicitly cancel/dismiss the underlying in-app browser session tied to the silent promptAsync() call before starting the interactive fallback attempt.
- Investigate available APIs in
expo-auth-session / expo-web-browser (e.g. WebBrowser.dismissBrowser() / dismissAuthSession()) to properly close orphaned sessions.
- Ensure chained/successive
switchOrg calls do not accumulate stuck browser sessions.
Rationale
Leaving the original silent-auth browser session alive causes overlapping browser/auth sessions, which can result in the UI getting stuck, unexpected browser dismiss behavior, or user confusion when switching organizations multiple times in a row.
Acceptance criteria
- Triggering the silent auth timeout in
switchOrg cleans up (dismisses/cancels) the original browser session before opening the interactive fallback.
- Chained org switches (multiple
switchOrg calls in succession) do not leave orphaned/stuck browser sessions.
- No regression to normal
login/register/switchOrg flows that do not hit the timeout.
References
Summary
In
lib/KindeAuthProvider.tsx, theauthenticate()helper racesrequest.promptAsync(...)against a timeout whenauthTimeoutMsis provided (used by the new silentswitchOrgflow withprompt: none). When the timeout wins the race, the underlyingAuthRequest.promptAsync(...)call is never cancelled or dismissed — the in-app browser/auth session opened for the silent attempt keeps running in the background.switchOrgthen falls back to an interactiveprompt: loginattempt, which opens a secondAuthRequest/browser session on top of the still-running first one. This is very likely the root cause of the "stuck browser" issue referenced in PR #134's description, where chained org switches can get stuck because Expo browsers opened for silent reauth aren't handled/closed correctly.Affected area
lib/KindeAuthProvider.tsx: theauthenticate()helper (silent-auth timeout race added forswitchOrg), and theswitchOrg()method that performs the silent-then-interactive fallback.Required changes
authTimeoutMstimeout fires (i.e., the timeout branch of thePromise.racewins), explicitly cancel/dismiss the underlying in-app browser session tied to the silentpromptAsync()call before starting the interactive fallback attempt.expo-auth-session/expo-web-browser(e.g.WebBrowser.dismissBrowser()/dismissAuthSession()) to properly close orphaned sessions.switchOrgcalls do not accumulate stuck browser sessions.Rationale
Leaving the original silent-auth browser session alive causes overlapping browser/auth sessions, which can result in the UI getting stuck, unexpected browser dismiss behavior, or user confusion when switching organizations multiple times in a row.
Acceptance criteria
switchOrgcleans up (dismisses/cancels) the original browser session before opening the interactive fallback.switchOrgcalls in succession) do not leave orphaned/stuck browser sessions.login/register/switchOrgflows that do not hit the timeout.References