[various] Allow resuming pending authorization after activity recreation on Android - #669
[various] Allow resuming pending authorization after activity recreation on Android#669moritz-j wants to merge 8 commits into
Conversation
- for auth with and without code exchange - only when receiving activity result with no pending operation, i.e. when no Result is available to send auth result to flutter - preserves null intent error because then no data is available to store
takes pending authorization data and returns it to new Result
afaik there is no scenario this can happen on iOS or macOS
MaikuB
left a comment
There was a problem hiding this comment.
Thanks for the PR. Can you update the example app so it shows how this could be used? This includes adding code comments to the example app where appropriate
There was a problem hiding this comment.
🟡 Changes recommended
Two critical Android issues and one moderate test issue remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds resumePendingAuthorization() to recover Android authorization results after Activity recreation, with typed Dart responses and iOS/macOS no-op support.
Changes:
- Adds the public resume API and response models.
- Implements Android pending-authorization handling.
- Adds platform mappings and method-channel tests.
File summaries
| File | Summary |
|---|---|
flutter_appauth/macos/flutter_appauth/Sources/flutter_appauth/FlutterAppauthPlugin.m |
Adds macOS no-op handling. |
flutter_appauth/lib/src/flutter_appauth.dart |
Exposes the resume API. |
flutter_appauth/lib/flutter_appauth.dart |
Exports resume response types. |
flutter_appauth/ios/flutter_appauth/Sources/flutter_appauth/FlutterAppauthPlugin.m |
Adds iOS no-op handling. |
flutter_appauth/ios/flutter_appauth/Sources/flutter_appauth/FlutterAppAuth.h |
Defines the resume method constant. |
flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java |
Implements pending authorization recovery. Critical (2 votes): preserve the exchange-flow flag before clearing state to report the correct failure code (lines 208 and 735). Critical (1 vote): persist pending authorization and exchange state across Activity/engine recreation (lines 92 and 738). |
flutter_appauth_platform_interface/test/method_channel_flutter_appauth_test.dart |
Tests response mapping. Moderate (2 votes): correct the epoch/timezone expectation at line 255. |
flutter_appauth_platform_interface/lib/src/method_channel_mappers.dart |
Adds response mapping helpers. |
flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart |
Invokes the channel and maps responses. Nit (1 vote): preserve the original stack trace when rethrowing platform exceptions. |
flutter_appauth_platform_interface/lib/src/flutter_appauth_platform.dart |
Adds the platform interface method. |
flutter_appauth_platform_interface/lib/src/authorization_resume_response.dart |
Defines typed resume responses. |
flutter_appauth_platform_interface/lib/flutter_appauth_platform_interface.dart |
Exports the new response model. |
Review details
Suppressed comments (3)
flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java:739
- On a recreated plugin, the request-scoped
clientSecretandallowInsecureConnectionsfields have reverted to their defaults because they are only populated by the original method call. This reusesprocessAuthorizationData, which reads those fields during the code exchange, so resumedauthorizeAndExchangeCodeflows for confidential clients or non-HTTPS endpoints fail. Persist these options inPendingAuthorizationand use them for the resumed exchange (or require them as resume input).
checkAndSetPendingOperation(RESUME_PENDING_AUTHORIZATION_METHOD, result);
processAuthorizationData(pendingAuth.response, pendingAuth.exception, pendingAuth.exchangeCode);
flutter_appauth/android/src/main/java/io/crossingthestreams/flutterappauth/FlutterAppauthPlugin.java:739
- If the app starts another operation after a response has been stashed but before it calls this method,
pendingOperationis non-null here. The code clears the stashed response beforecheckAndSetPendingOperationthrows; the outer catch then completes the unrelated operation viafinishWithError, while theresumePendingAuthorizationResult is never completed. Check for an in-flight operation and fail the resume call without consuming the pending response.
final PendingAuthorization pendingAuth = pendingAuthorization;
pendingAuthorization = null;
checkAndSetPendingOperation(RESUME_PENDING_AUTHORIZATION_METHOD, result);
processAuthorizationData(pendingAuth.response, pendingAuth.exception, pendingAuth.exchangeCode);
flutter_appauth_platform_interface/lib/src/method_channel_flutter_appauth.dart:113
- The previous implementation used
rethrowfor platform exceptions without usable details. Moving this branch into a helper and usingthrow eresets the Dart stack trace to the helper (the identical branch below has the same problem), changing the existing pass-through behavior and making caller failures harder to diagnose. Preserve the original stack trace when delegating this exception, or keep the pass-through rethrow in the caller.
Never _mapAndThrowPlatformException(PlatformException e) {
if (e.details == null) {
throw e;
}
- Files reviewed: 12/12 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Hey @MaikuB, thanks for the review. I have added this feature to the example app and fixed 2/3 Copilot findings (the 3rd being not really useful). |
We have encountered an issue where the auth response is not handled after returning from the browser back to the app. This happens when the app's Activity is killed by Android (e.g. for clearing up memory). This PR adds a resumePendingAuthorization() method that allows the app to get the "missed" auth response after reinitialising itself.
Steps to reproduce:
=> Browser closes, but app doesn't receive the auth response.
Behaviour with fix: