xamarin / Xamarin.Auth

Xamarin.Auth
Apache License 2.0
542 stars 350 forks source link

Android 12 issue - Xamarin.Auth.AuthenticatorCompletedEventArgs return null account object and IsAuthenticated flag false #462

Open jhaprasun opened 2 years ago

jhaprasun commented 2 years ago

Xamarin.Auth Issue

In Android 12 OS devices, after successful login and allowed google drive consent our callback account object is null and IsAuthenticated flag is false. Though it is working fine for Android version below 12. For us this issue is a release blocker. Let us know if there is any workaround. Many thanks in advance.

Version

Steps to reproduce

1. void IGoogleOAuthSignIn.ShowGoogleAuthSignInPage() { CloudSettings.Authenticator = new OAuth2Authenticator( clientId: Constants.ClientId, clientSecret: string.Empty, scope: this.DriveService.Scope.Drive + " " + DriveService.Scope.DriveAppdata + " " + "https://www.googleapis.com/auth/userinfo.email", authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/auth"), redirectUrl: new Uri("com.xx.xxxxxx:/oauth2redirect"), accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token"), isUsingNativeUI: true);

        CloudSettings.Authenticator.Completed += this.OnOAuthCompleted;
        CloudSettings.Authenticator.Error += this.OnOAuthError;
        Intent intent = CloudSettings.Authenticator.GetUI(CurrentActivity);
        intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
        CurrentActivity.StartActivity(intent);

} 2. private void OnOAuthCompleted(object sender, AuthenticatorCompletedEventArgs eventArgs) { if (CloudSettings.Authenticator != null) { CloudSettings.Authenticator.Completed -= this.OnOAuthCompleted; CloudSettings.Authenticator.Error -= this.OnOAuthError; }

        if (eventArgs.IsAuthenticated)
        {
            if (CloudSettings.IsCalledFromSettingsPage)
            {
                MessagingCenter.Send<object>(false, Constants.FinishButtonVisible);
            }
            else
            {
                MessagingCenter.Send<object, bool>(this, Constants.DisplayActivityIndicator, true);
            }

            this.GetLoggedInUserData(eventArgs);
        }
        else
        {
            MessagingCenter.Send<object>(false, Constants.DisableBackupToggle);
            AppCenterHelper.LogEvent("Error: OAuth Sign-In", "OAuthSignIn.OnOAuthCompleted", "Authentication failed.");
        }
    }

3.

Platform:

Expected behaviour

Account object shouldn't be null and IsAuthenticated should be true

Tell us what should happen

Actual behaviour

Account object is null and IsAuthenticated flag is false

Tell us what happens instead Can you also include a screen shot?

Correct behavior with ANDROID version below 10 InCorrectbehaviourWith ANDROID-12

IF IT IS A NEW FEATURE REQUEST, INCLUDE THIS PART:

Feature description

Write a description of the feature. How should it work? How should it look? Include some graphics if this could help!

jhaprasun commented 2 years ago

Any workaround for above mentioned issue?

cristiproj commented 2 years ago

I am interested in this as well. @jhaprasun Have you found a workaround?

jhaprasun commented 2 years ago

Not yet... waiting for fix

tolyg commented 2 years ago

I'm waiting for fix too. It may be related to this issue and fix

jhaprasun commented 2 years ago

Not really. Here callback account object is null and IsAuthenticated flag is false

michaelstonis commented 2 years ago

I found a workaround that is good enough.

You may have an activity that has an IntentFilter on it to listen for some custom DataScheme that functions as a listener for the OAuth result. Grab the attribute from that class and copy it to your MainActivity or whatever you use as the main activity entry point to your app.

image

In your main activity, update the Activity attribute and add LaunchMode = LaunchMode.SingleTask.

image

In your activity that had the IntentFilter originally, copy the code that is currently in the OnResume method. In your main activity, we will override the OnNewIntent(Intent intent) method, paste in the code. We need to remove any code around managing activities and just want to keep the pieces related to OAuth. The code should look something like the below example. The way that you are resolving the page variable is very likely different though, so just do whatever you are doing today to get the object, so you can call into OnPageLoading.

image

At this point, comment out or delete the other activity that originally had the IntentFilter on it.

Open the Properties/AndroidManifest.xml file using a text editor. Inside the manifest attribute, make sure you have the following queries element.

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <queries>
        <!-- Required for API Level 30 to make sure we can detect browsers
        (that don't support custom tabs) -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
        </intent>
        <!-- Required for API Level 30 to make sure we can detect browsers that support custom tabs -->
        <!-- https://developers.google.com/web/updates/2020/07/custom-tabs-android-11#detecting_browsers_that_support_custom_tabs -->
        <intent>
            <action android:name="android.support.customtabs.action.CustomTabsService" />
        </intent>
    </queries>
</manifest>

That should do it for you.

Basically, what is happening on Android 12 is that you are leaving your application, then starting up everything fresh again and it is losing the context that it needs. This should continue to work on older versions, but I haven't thoroughly tested it.

mwisnicki0 commented 2 years ago

@michaelstonis thanks man, just tested it in own app and working very well

jhaprasun commented 2 years ago

I followed all the steps mentioned for resolution but its not working for me and getting an error- "Error Authenticating - Invalid grant"

michaelstonis commented 2 years ago

I followed all the steps mentioned for resolution but its not working for me and getting an error- "Error Authenticating - Invalid grant"

This is almost certainly to do with the OAuth endpoint. It could be that you are requesting access for an invalid user or permissions that are not available or one of a lot of other things. If you are getting that response though, you are at least further along as you would not have any values before.