AzureAD / microsoft-authentication-library-for-android

Microsoft Authentication Library (MSAL) for Android
http://aka.ms/aadv2
MIT License
214 stars 125 forks source link

acquireToken uses caching, despite documentations states this method skips cache lookup #2197

Open roman-behul opened 2 weeks ago

roman-behul commented 2 weeks ago

Issue Report: Unexpected Behavior with acquireToken Method in MSAL SDK

Describe the bug Our app is in development. We are using MSAL SDK version com.microsoft.identity.client:msal:5.1.0 to retrieve tokens via the createSingleAccountPublicClientApplication and acquireToken methods. We utilize the withAuthorizationQueryStringParameters method to add a nonce to the requested token. Our goal is to add a different nonce with each acquireToken call to prevent replay attacks. However, when the device has the Company Portal installed, the nonce in the JWT token remains the same as the first call to acquireToken. This suggests that some caching is being applied, despite the documentation stating that the interactive flow will skip the cache lookup.

Smartphone (please complete the following information):

Stacktrace We have found these logs when running the app with no Company Portal app installed - case when everything works as expected. We see no logs/tags when running the app on a device with the Company Portal installed.

SchemaUtil:getAlternativeAccountId       sk.tb.ib.tatraandroid.ema.dev   W  [2024-10-02 12:15:09 - thread_id: 165, correlation_id: f7ee4492-7c90-41af-837e-f4a4d5847436 - Android 34] alternative_account_id was null.
SchemaUtil:getAvatarUrl                  sk.tb.ib.tatraandroid.ema.dev   W  [2024-10-02 12:15:09 - thread_id: 165, correlation_id: f7ee4492-7c90-41af-837e-f4a4d5847436 - Android 34] Avatar URL was null.
SharedPreferencesAccountCredentialCache  sk.tb.ib.tatraandroid.ema.dev   W  [2024-10-02 12:15:09 - thread_id: 165, correlation_id: f7ee4492-7c90-41af-837e-f4a4d5847436 - Android 34] Deserialization failed. Skipping Credential

To Reproduce Steps to reproduce the behavior:

  1. Install the Company Portal on the device.
  2. Use the acquireToken method with withAuthorizationQueryStringParameters to add a nonce.
  3. Call acquireToken multiple times with different nonces.
  4. Observe that the nonce in the token remains the same as the first call.

Expected behavior We expect to always receive the actual nonce that was added when calling acquireToken. Exposing some of the underlying Java SDK API that allows us to clear or disable the cache might also help.

Actual Behavior The nonce in the jwt token remains the same as the first call to acquireToken, indicating that some caching is being applied.

Code Snippets:

Method for calling acquireToken:

private fun getTokenInteractive(
    activity: BaseActivity,
    nonceBE: String,
    onSuccess: TokenResult,
    onError: (String) -> Unit
) {
    val app = getMsalApp(activity)
    val params = AcquireTokenParameters.Builder()
        .withScopes(listOf(
            "User.Read",
            "User.Read.All",
            "CustomAuthenticationExtension.Read.All",
            "openid",
            "profile",
            "email"
        ))
        .forAccount(app.currentAccount.currentAccount)
        .withCallback(authenticationCallback(onSuccess, onError))
        .startAuthorizationFromActivity(activity)
        .withAuthorizationQueryStringParameters(listOf(
            AbstractMap.SimpleEntry("nonce", nonceBE),
            AbstractMap.SimpleEntry("tbnonce", nonceBE)
        ))
        .build()

    app.acquireToken(params)
}

Method for initializing ISingleAccountPublicClientApplication:

private fun getMsalApp(context: BaseActivity): ISingleAccountPublicClientApplication {
    if (msalApp == null) {
        msalApp = PublicClientApplication.createSingleAccountPublicClientApplication(
            context,
            if (FeatureToggles.isEmaIntuneMsalConfigProd)
                R.raw.msal_config_prod
            else
                R.raw.msal_config_test
        )
    }
    return msalApp!!
}

Screenshots Screenshot_20241002_153205

Additional context

shahzaibj commented 1 week ago

Hi @roman-behul, a few questions to understand your use case better:

roman-behul commented 1 week ago

Hi @shahzaibj ,

let me add more context before answering your questions:

We are implementing LOB mobile native app (FE app) , which is available via work profile - Intune company portal. This FE app, running in employee device, is requesting token from EntraID IDP . This token is then presented to on-prem BE app . Purpose of this token usage is to have trust on BE side, that BE app is called from already authenticated user-employee . FE app is requesting token using MSAL method and in request is provided also "nonce" attribute . Nonce is generated by BE app and sent to FE app. FE app sends nonce in request for token - this is standard pattern how to prevent replay attack. And this approach is described also in documentation of this used method : app.acquireToken().

Our issue is, that in returned token is not correct nonce, because it seems that token is returned from some cache. AcquireToken method, should obtain token interactive way (no silent). This is also mentioned in documentation.

So our IDP is EntraID. Our users are defined there. (No federation in this use case)