Skip to content

[various] Allow resuming pending authorization after activity recreation on Android - #669

Open
moritz-j wants to merge 8 commits into
MaikuB:masterfrom
BrickmakersGmbH:fix/android_resume_auth
Open

[various] Allow resuming pending authorization after activity recreation on Android#669
moritz-j wants to merge 8 commits into
MaikuB:masterfrom
BrickmakersGmbH:fix/android_resume_auth

Conversation

@moritz-j

Copy link
Copy Markdown

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:

  1. Enable "Don't keep activities" in Android developer settings.
  2. Start auth flow.
  3. Complete authentication.

=> Browser closes, but app doesn't receive the auth response.

Behaviour with fix:

  1. Browser auth flow started
  2. Activity killed in background
  3. Browser flow finishes
  4. Activity is recreated and flutter reinitialized
  5. App calls resumePendingAuthorization() and receives response
  6. User logged in

moritz-j added 4 commits July 17, 2026 11:22
- 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
MaikuB requested a lite review from Copilot September 13, 2026 06:04

@MaikuB MaikuB left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 clientSecret and allowInsecureConnections fields have reverted to their defaults because they are only populated by the original method call. This reuses processAuthorizationData, which reads those fields during the code exchange, so resumed authorizeAndExchangeCode flows for confidential clients or non-HTTPS endpoints fail. Persist these options in PendingAuthorization and 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, pendingOperation is non-null here. The code clears the stashed response before checkAndSetPendingOperation throws; the outer catch then completes the unrelated operation via finishWithError, while the resumePendingAuthorization Result 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 rethrow for platform exceptions without usable details. Moving this branch into a helper and using throw e resets 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.

@moritz-j
moritz-j requested a review from MaikuB September 14, 2026 08:37
@moritz-j

Copy link
Copy Markdown
Author

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants