Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
roman-behul opened this issue Oct 2, 2024 · 2 comments

Comments

@roman-behul
Copy link

roman-behul commented Oct 2, 2024

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

  • Device: Pixel 8 Pro emulator
  • Android Version: 34 SDK
  • Browser: Chrome tabs
  • MSAL Version: com.microsoft.identity.client:msal:5.1.0

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
Copy link
Contributor

shahzaibj commented Oct 9, 2024

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

  • How is this nonce ultimately included in the token? Is it a claim inside the Access Token JWT?
  • Are you using federation? What's your Identity Provider?
  • Who's actually including this nonce in the token? Is it Microsoft Entra or rather your own Identity Provider?

@roman-behul
Copy link
Author

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)

  • How is this nonce ultimately included in the token? Is it a claim inside the Access Token JWT?
    -- nonce come together with token. Token is a claim returned inside getIdToken, that is property of IAccount returned by IAuthenticationResult. Here is an sample returned in idToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1jN2wzSXo5M2c3dXdnTmVFbW13X1dZR1BrbyJ9.eyJhdWQiOiJiYWZjYzZiMC00NTI2LTQ1N2EtYjBiYS1hNDdjODcwZDdjNzgiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOWI1MTFmZGEtZjBiMS00M2E1LWIwNmUtMWU3MjBmNjQ1MjBhL3YyLjAiLCJpYXQiOjE3Mjc4NzM3NDEsIm5iZiI6MTcyNzg3Mzc0MSwiZXhwIjoxNzI3ODc3NjQxLCJhY2N0IjowLCJhaW8iOiJBVlFBcS84WUFBQUFCaGp6YVFZWDg4bk1lSFZ0c3UvdXd0ejZRRU5LSnNiSjNuRzd4VGwzMlpueHdNN25aU2hCc2s2eVVyQ3RiQ2hhNzhZdHkwRXI0WFFYaDhNQURrQXY0MWNXZDFrb0hDYUkrQTdZeHhlclpsbz0iLCJhdXRoX3RpbWUiOjE3Mjc4Njg5MzYsImN0cnkiOiJTSyIsImVtYWlsIjoia2F0YXJpbmFfbWFkZWxvdmFAdGF0cmFiYW5rYS5zayIsImZhbWlseV9uYW1lIjoiTcOBREVMT1bDgSIsImdpdmVuX25hbWUiOiJLYXRhcsOtbmEiLCJpcGFkZHIiOiI1LjE3OC41Ni4xNDkiLCJsb2dpbl9oaW50IjoiTy5DaVEzT1Roak0yTTFPQzAwTVdKbUxUUXpaR1l0T0dJNU55MHlaak0yTldFNFlqQmhabUlTSkRsaU5URXhabVJoTFdZd1lqRXRORE5oTlMxaU1EWmxMVEZsTnpJd1pqWTBOVEl3WVJvZmEyRjBZWEpwYm1GZmJXRmtaV3h2ZG1GQWRHRjBjbUZpWVc1cllTNXpheUJNIiwibmFtZSI6IkthdGFyw61uYSBNw4FERUxPVsOBIiwibm9uY2UiOiJpUllHa2p2RnF1Y21yN3dZVDJFX0RFT3E3bjY3TTZrMmxZenZMMHBIRmprIiwib2lkIjoiNzk4YzNjNTgtNDFiZi00M2RmLThiOTctMmYzNjVhOGIwYWZiIiwib25wcmVtX3NpZCI6IlMtMS01LTIxLTEyMjkyNzI4MjEtNjAyMTYyMzU4LTcyNTM0NTU0My0zMDQ0OTEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJrYXRhcmluYV9tYWRlbG92YUB0YXRyYWJhbmthLnNrIiwicmgiOiIwLkFRSUEyaDlSbTdId3BVT3diaDV5RDJSU0NyREdfTG9tUlhwRnNMcWtmSWNOZkhnQ0FCTS4iLCJzaWQiOiIxNWU2Yjg0Yy0wMWE5LTQ4NjQtYjA1OC1jN2FlNDRmMDM5N2EiLCJzdWIiOiJzbHlySWJwelZnNWxYbzB0UzJXZEw4eUJleUFWR3l4bkphMzh5WmltUmtVIiwidGVuYW50X2N0cnkiOiJBVCIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJFVSIsInRpZCI6IjliNTExZmRhLWYwYjEtNDNhNS1iMDZlLTFlNzIwZjY0NTIwYSIsInVwbiI6ImthdGFyaW5hX21hZGVsb3ZhQHRhdHJhYmFua2Euc2siLCJ1dGkiOiI0bkFCck1EcW1raVFERDVnYWhVM0FBIiwidmVyIjoiMi4wIiwidmVyaWZpZWRfcHJpbWFyeV9lbWFpbCI6WyJrYXRhcmluYV9tYWRlbG92YUB0YXRyYWJhbmthLnNrIl0sInZlcmlmaWVkX3NlY29uZGFyeV9lbWFpbCI6WyJ0YjA0NzE4MEB0YXRyYWJhbmthLnNrIiwiSVNLQTE1NTRAcmJpbnRlcm5hdGlvbmFsLm9ubWljcm9zb2Z0LmNvbSJdLCJ4bXNfdHBsIjoiZW4ifQ.bhX0JbUi0KFNfZbaesSke8oWO_iTHW9ecspW0mp4PMS6MfYcrY-JkjD4K3e3B7nYA_UlusL_PCAOVzlfxPuUkaxF4xQukzh1ZpIFLLOt-Jlv0aEI1AdL9SaFQInic42Zyvjhy-Ncip7IpwQ92MSssKo9R3-kPQ-cElmTqFjD9NNYx09K04WqOZ7Jcp4Cd91EokLhEvnpy_5-9H14myrYrOB_pOJO1j4H7i7LESIto4iheQ87pdT3bm-S1jmIbxkq9n7ne12Ng48jFAOq1vERpl8lYVugZvP-eMTesMSxBSH_6NICkneaugcUHe-yz6iOLfkbFYsDaZTWlAVfu74K5Q. You can see it's details when pasted here: https://jwt.io/
  • Are you using federation? What's your Identity Provider?
    -- our IDP is EntraID. Our users are defined there (No federation in this use case)
  • Who's actually including this nonce in the token? Is it Microsoft Entra or rather your own Identity Provider?
    -- FE app sends nonce in request for token. Entra should obtain the request with nonce and issue token with included nonce value in claim "nonce". This is standard pattern how to prevent replay attack. And this approach is described also in documentation of this used method :app.acquireToken()

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

No branches or pull requests

2 participants