microsoft-authentication-library-for-android: The acquireToken method doesn't work if the app uses "withFragmemt"
Details We are going to make b2c authorization in our Android app and we faced with issue with “IMultipleAccountPublicClientApplication.acquireToken” method and fragment.
Smartphone (please complete the following information):
- Device: Samsun M51
- Android Version: 12,
- MSAL Version: 4.0.5
Config
{ "broker_redirect_uri_registered": false, "client_id" : "****", "authorization_user_agent" : "WEBVIEW", "redirect_uri" : "****", "authorities" : [ { "type": "B2C", "authority_url": "https://schcoad.b2clogin.com/schcoad.onmicrosoft.com/.......", "default": true } ] }
Stacktrace
W/MSALLogger: AuthorizationFragment:onCreate | [2022-11-11 13:11:47 - thread_name: main, correlation_id: UNSET - Android 31] No stored state. Unable to handle response W/AuthorizationFragment:onCreate: [2022-11-11 13:11:47 - thread_name: main, correlation_id: UNSET - Android 31] No stored state. Unable to handle response E/MSALLogger: AuthorizationFragment#finish | [2022-11-11 13:11:47 - thread_name: main, correlation_id: UNSET - Android 31] Logged as error to capture 'cause'; Exception occurred when removing ourselves from provided FragmentManager java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1636) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1666) at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:317) at com.microsoft.identity.common.internal.providers.oauth2.AuthorizationFragment.finish(AuthorizationFragment.java:136) at com.microsoft.identity.common.internal.providers.oauth2.AuthorizationFragment.onCreate(AuthorizationFragment.java:94) at com.microsoft.identity.common.internal.providers.oauth2.WebViewAuthorizationFragment.onCreate(WebViewAuthorizationFragment.java:104) at androidx.fragment.app.Fragment.performCreate(Fragment.java:2981) at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:474) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:257) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1840) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1758) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701) at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:488) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8663) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
To Reproduce
-
Create the fragment where you would like to have the WebView. As example, in sample app. it can be “MultipleAccountModeFragment” from the sample app.
-
Set the WebView client for “authorization_user_agent” : “WEBVIEW”,
-
Add the “withFragment” to
val parameters: AcquireTokenParameters = AcquireTokenParameters.Builder() .withScopes(AzureUserAuthorizationApi.SCOPES) .startAuthorizationFromActivity(requireActivity())// we are inside fragment .withCallback(b2cAuthenticationCallback) *.withFragment(*this*)// this equals MultipleAccountModeFragment* .withPrompt(Prompt.LOGIN) .build()
-
call IMultipleAccountPublicClientApplication.acquireToken(parameters)
Expected behavior I would like to have the WebView is run inside the fragment/over MultipleAccountModeFragment. Also, I checked all the provided documentation, samples and I’ve not found anything how to use “withFragment” method correctly. I kindly ask to document it and it can save a lot of time for developers. Right know it doesn’t work.
Actual Behavior The error log message is observed, the spinner is running.
I found that it by some reasons “savedInstanceState” and “mInstanceState” is null and it closes itself onCreate.
AuthorizationFragment.java
if (savedInstanceState == null && mInstanceState == null) { Logger.warn(methodTag, "No stored state. Unable to handle response"); **finish();** return; }
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 23 (1 by maintainers)
I found the workaround. Just create the same class in your project with the same package and Java runtime will use your class instead. Please take the fix here: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/pull/1704
I still see the error about fragment transaction, but it works.
@VolodaUa Thanks for the feedback on the documentation and sample. We are taking a look and will provide you with updates.
I was able to get this working in release. TLDR; it takes some work.
For my purposes, I wanted to replace both
AndroidAuthorizationStrategy.java
(this bug) andWebViewAuthorizationFragment.java
(I needed access to the webview) with my own implementations.You may want to get a coffee (or beverage of your choice) before attempting the following:
Removing the necessary files from the library
.gradle/caches/modules-2/files-2.1/com.microsoft.identity/common/9.0.0
)classes.jar
exists. I renamed its extension to a .zip and unzipped its contents..class
files for bothAndroidAuthorizationStrategy
andWebViewAuthorizationFragment
jar cvf classes.jar classes
jar cvf microsoft-common.aar common-9.0.0
Importing the AAR in your project successfully
I added
microsoft-common.aar
toapp/libs/
in my project (libs may be a new directory that you need to create).In the
dependencies
section of my appbuild.gradle
:Result
Now I can build a release build with both
com.microsoft.identity.common.internal.providers.oauth2.AndroidAuthorizationStrategy.java
andcom.microsoft.identity.common.internal.providers.oauth2.WebViewAuthorizationFragment.java
existing in my project. You can grab these classes from https://github.com/AzureAD/microsoft-authentication-library-common-for-android/tree/dev/common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2For those who trust a random on GitHub providing them with a modified AAR, I have attached mine below. For those who don’t, just follow the Removing the necessary files from the library steps above.
microsoft-common.aar.zip
I did this and it worked beautifully. Thanks!
Wow, you’re awesome. Thanks for bringing back a workaround. To clarify, you just created
src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AndroidAuthorizationStrategy.java
in your local source with this fix?@negoe Could you please tell me whether the issue is fixed? I don’t see any pull request with the fix, but the ticket was closed.
Facing the same issue and the work-around of having a local copy of
AndroidAuthorizationStrategy
is failing in the Release builds.@negoe Sorry for mentioned you. This issue is critical for our customer future release. Could someone from team to take a look?