teranetsrl / oauth2_client

Simple Dart library for interacting with OAuth2 servers.
BSD 2-Clause "Simplified" License
91 stars 111 forks source link

Crash immediately after starting on iOS in SwiftFlutterWebAuthPlugin.handle #182

Open dlatikaynen opened 4 months ago

dlatikaynen commented 4 months ago

Works perfectly well on Android.

0   flutter_web_auth_2              0x00000001023852dc Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value + 0 (<compiler-generated>:0)
1   flutter_web_auth_2              0x00000001023852dc closure #1 in SwiftFlutterWebAuth2Plugin.handle(_:result:) + 1452 (<compiler-generated>:0)
2   flutter_web_auth_2              0x0000000102385788 thunk for @escaping @callee_guaranteed (@in_guaranteed URL?, @guaranteed Error?) -> () + 192 (<compiler-generated>:0)
3   AuthenticationServices          0x0000000202097d88 -[ASWebAuthenticationSession _startDryRun:] + 592 (ASWebAuthenticationSessionIOS.m:177)
4   flutter_web_auth_2              0x0000000102386fb4 specialized SwiftFlutterWebAuth2Plugin.handle(_:result:) + 3492 (SwiftFlutterWebAuth2Plugin.swift:100)
5   flutter_web_auth_2              0x0000000102385338 specialized SwiftFlutterWebAuth2Plugin.handle(_:result:) + 16 (<compiler-generated>:0)
6   flutter_web_auth_2              0x0000000102385338 @objc SwiftFlutterWebAuth2Plugin.handle(_:result:) + 84

crashlog.txt

oauth2_client: ^3.2.2

class CAATSOAuth2Client extends OAuth2Client {
  CAATSOAuth2Client({required String authorizeUrl, required String tokenUrl, required String refreshUrl, required String revokeUrl})
      : super(
            authorizeUrl: authorizeUrl,
            tokenUrl: tokenUrl,
            refreshUrl: refreshUrl,
            revokeUrl: revokeUrl,
            redirectUri: AuthConstants.loginRedirectUri,
            customUriScheme: AuthConstants.uriScheme,
            credentialsLocation: CredentialsLocation.body);
}
  late String authorizationEndpoint = "";
  late String tokenEndpoint = "";
  late String refreshEndpoint = "";
  late String revokeEndpoint = "";
  late String endSessionEndpoint = "";

  late CAATSOAuth2Client client;
  late OAuth2Helper helper;
  late ConnectedSystem connectedSystem;

  Future<void> setup(ConnectedSystem connectedSystem, String clientId, String clientSecret) async {
    final storageKey = uuid.v1();
    this.connectedSystem = connectedSystem;

    client = CAATSOAuth2Client(
        authorizeUrl: authorizationEndpoint,
        tokenUrl: tokenEndpoint,
        refreshUrl: refreshEndpoint,
        revokeUrl: revokeEndpoint);

    helper = OAuth2Helper(client,
        clientId: clientId,
        clientSecret: clientSecret,
        scopes: [...AuthConstants.scopes],
        webAuthOpts: {"preferEphemeral": true},
        enablePKCE: true,
        enableState: true,
        grantType: OAuth2Helper.authorizationCode,
        tokenStorage: TokenStorage(storageKey));

    await fetchConfiguration(connectedSystem.host.idpAuthority);
    client.authorizeUrl = authorizationEndpoint;
    client.tokenUrl = tokenEndpoint;
    client.refreshUrl = refreshEndpoint;
    client.revokeUrl = revokeEndpoint;
  }
class TokenCubit extends Cubit<AccessTokenResponse?> {
  TokenCubit() : super(null);

  Future<AccessTokenResponse?> login(
    ConnectedSystem connectedSystem,
    String clientId,
    String clientSecret,
  ) async {
    if (await SessionManager.startSession(connectedSystem, clientId, clientSecret)) {
      debugPrint('#*#*# before -- fetchtoken');

      final tokenRes = await SessionManager.currentSession!.helper.fetchToken();

      if (!isClosed) {
        emit(tokenRes);
      }

      return tokenRes;
    }

    debugPrint('#*#*# before -- login return null');

    return null;
  }
}

Since this is some kind of null reference exception (or "unpacking-asserted-not-null value which is actually null") kind of error, maybe there is now a version mismatch between this and the underlying plugin, and we feed it something which is not a mandatory input in oauth2_client but (now) is in one of the underlying functions?

mibos-kremer commented 2 months ago

Experiencing the same issue here.

0   flutter_web_auth_2              0x0000000102cfd6bc Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value + 0 (SwiftFlutterWebAuth2Plugin.swift:0)
1   flutter_web_auth_2              0x0000000102cfd6bc closure #1 in SwiftFlutterWebAuth2Plugin.handle(_:result:) + 1312 (SwiftFlutterWebAuth2Plugin.swift:29)
2   flutter_web_auth_2              0x0000000102cfdb64 thunk for @escaping @callee_guaranteed (@in_guaranteed URL?, @guaranteed Error?) -> () + 192 (<compiler-generated>:0)
3   AuthenticationServices          0x00000002065cbb70 -[ASWebAuthenticationSession _cancelWithError:] + 40 (ASWebAuthenticationSessionIOS.m:312)
4   AuthenticationServices          0x00000002065cb9f4 -[ASWebAuthenticationSession _startDryRun:] + 644 (ASWebAuthenticationSessionIOS.m:228)
5   flutter_web_auth_2              0x0000000102cff0e8 specialized SwiftFlutterWebAuth2Plugin.handle(_:result:) + 2904 (SwiftFlutterWebAuth2Plugin.swift:100)
6   flutter_web_auth_2              0x0000000102cfd714 specialized SwiftFlutterWebAuth2Plugin.handle(_:result:) + 16 (<compiler-generated>:0)
7   flutter_web_auth_2              0x0000000102cfd714 @objc SwiftFlutterWebAuth2Plugin.handle(_:result:) + 84
8   Flutter                         0x00000001037c9a38 0x1031e8000 + 6167096
9   Flutter                         0x000000010322bc00 0x1031e8000 + 277504

We partially worked around the crash by adding a login button instead of automatically logging in on startup, but it still crashes occasionally.

ghSingleOps commented 6 days ago

I am seeing this issue on app startup too.

The code that is generated for SwiftFlutterWebAuth2Plugin specifies a completionHandler that assumes a session exists. It crashes on this line:

(sessionToKeepAlive as! ASWebAuthenticationSession).cancel()

The problem is that a prior session was NOT established yet. The completionHandler is being called with an error related to app window management:

Error Domain=com.apple.AuthenticationServices.WebAuthenticationSession Code=3 "The UIWindowScene for the returned window was not in the foreground active state." UserInfo={NSDebugDescription=The UIWindowScene for the returned window was not in the foreground active state.}

A fix that works for me is to determine when the app actually reports that it is in foreground, and only attempt a network operation at that point. I used a solution that is specified here: https://stackoverflow.com/questions/51835039/how-do-i-check-if-the-flutter-application-is-in-the-foreground-or-not