Repository navigation
feat: allow to resume the request if androdi OS kills the process - #1146
Conversation
🦋 Changeset detectedLatest commit: 18b6780 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
||
| private static final AtomicReference<Intent> sStashedAuthorizationResult = new AtomicReference<>(); | ||
|
|
||
| public static void stashAuthorizationResult(Intent data) { |
There was a problem hiding this comment.
| public static void stashAuthorizationResult(Intent data) { | |
| public static void stashAuthorizationResult(final Intent data) { |
|
|
||
| authService.performTokenRequest(tokenRequest, new AuthorizationService.TokenResponseCallback() { | ||
| @Override | ||
| public void onTokenRequestCompleted(TokenResponse resp, AuthorizationException ex) { |
There was a problem hiding this comment.
| public void onTokenRequestCompleted(TokenResponse resp, AuthorizationException ex) { | |
| public void onTokenRequestCompleted(final TokenResponse resp, final AuthorizationException ex) { |
| } = {}) => { | ||
| if (Platform.OS !== 'android') { | ||
| return Promise.resolve(null); | ||
| } | ||
|
|
||
| validateHeaders(customHeaders); | ||
| validateConnectionTimeoutSeconds(connectionTimeoutSeconds); | ||
|
|
||
| return wrapNativeAuthPromise( | ||
| RNAppAuth.resumePendingAuthorize( | ||
| additionalParameters, | ||
| convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds), |
There was a problem hiding this comment.
| } = {}) => { | |
| if (Platform.OS !== 'android') { | |
| return Promise.resolve(null); | |
| } | |
| validateHeaders(customHeaders); | |
| validateConnectionTimeoutSeconds(connectionTimeoutSeconds); | |
| return wrapNativeAuthPromise( | |
| RNAppAuth.resumePendingAuthorize( | |
| additionalParameters, | |
| convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds), | |
| } = {}) => { | |
| const platform = Platform.OS | |
| if (platform !== 'android') { | |
| return Promise.resolve(null); | |
| } | |
| validateHeaders(customHeaders); | |
| validateConnectionTimeoutSeconds(connectionTimeoutSeconds); | |
| return wrapNativeAuthPromise( | |
| RNAppAuth.resumePendingAuthorize( | |
| additionalParameters, | |
| convertTimeoutForPlatform(platform, connectionTimeoutSeconds), |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Resumed flows currently break client-authenticated and skipped-exchange configurations, and the background launch retains a stale Activity reference.
Review effort: Balanced
Findings: 3
Open (5)
Preserve skipCodeExchange when resuming authorization · New Resume authorization with configured client authentication · New Use current Activity immediately before launching authorization · New Import Intent in the Kotlin integration example · New Add platform wrapper coverage to index.spec.js · New
What changed in this PR
Adds Android authorization recovery after process termination and moves Custom Tab setup off the UI thread.
Changes:
- Adds
resumePendingAuthorize()to JavaScript, TypeScript, and Android. - Stashes authorization results until React Native initializes.
- Documents required Android activity forwarding.
| File | Description |
|---|---|
index.js |
Exposes the resume API. |
index.d.ts |
Defines resume API types. |
RNAppAuthModule.java |
Implements result recovery and background Custom Tab setup. |
authorization.md |
Documents setup and usage. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| export const resumePendingAuthorize = ({ | ||
| additionalParameters, | ||
| dangerouslyAllowInsecureHttpRequests = false, | ||
| customHeaders, | ||
| connectionTimeoutSeconds, | ||
| } = {}) => { |
Match the existing wrapper test mocks and address the platform lookup review suggestion.


Description
On Android, the OS can kill the app process while the user is away in the Custom Tab completing
login (e.g. under memory pressure). When the app restarts, React Native drops the resulting
activity result because it arrives before the JS context is ready
(react/react-native#30277), so the in-flight
authorize()promise is lost and the user'slogin silently fails to complete even though the browser already returned a valid response.
This adds a
resumePendingAuthorize()API to recover from that case:RNAppAuthModulenow stashes the raw activity result in a static field(
stashAuthorizationResult) when it's received with no in-flightauthorize()promise on themodule instance, instead of discarding it.
resumePendingAuthorizenative method and JS/TS export claim that stashed result,re-derive the token request from the
AuthorizationResponse(which carries the PKCE codeverifier), and complete the token exchange.
MainActivity.onActivityResulttoRNAppAuthModule.stashAuthorizationResultand callresumePendingAuthorize()on startup;it resolves
null(safe to call unconditionally) when there's nothing to resume, and alwaysresolves
nullon iOS.AuthorizationServiceconstruction andcreateCustomTabsIntentBuilder()off themain thread in
authorize(), since both are@WorkerThreadcalls that could block the UI forup to a second.
MainActivitywiring indocs/docs/usage/authorization.md.Steps to verify
MainActivity.onActivityResultto callRNAppAuthModule.stashAuthorizationResultas shown in the updated docs, and call
resumePendingAuthorize()on app startup.authorize()against a test IdP, and while the Custom Tab is open, kill the app processfrom Android Studio's Logcat ("Terminate Application") or via
adb shell am kill <package>.resumePendingAuthorize()should resolve with a valid token response instead of the flow silently failing.
authorize()flow still resolves as before, and thatresumePendingAuthorize()resolvesnullwhen called with nothing pending.resumePendingAuthorize()resolvesnullimmediately on iOS.