Fix: WPJ's BrokerDiscovery cache crash due to shared predefined encryption key with MSAL, Fixes AB#3577391#3081
Fix: WPJ's BrokerDiscovery cache crash due to shared predefined encryption key with MSAL, Fixes AB#3577391#3081
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR addresses a crash in WPJ/BrokerDiscovery caused by Company Portal setting a global MSAL predefined encryption key that later becomes unavailable, leading to unhandled decryption failures.
Changes:
- Add a “keystore-only” mode to the Android storage encryption manager and surface missing-key decryption as
ClientException(DECRYPTION_FAILED)instead ofIllegalStateException. - Add a new platform components factory method to build components with keystore-only encryption and wire Device Registration to use it.
- Add/extend tests, including an end-to-end test that validates IPC fallback and cache self-healing on decryption failure.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| common/src/main/java/com/microsoft/identity/common/crypto/AndroidAuthSdkStorageEncryptionManager.java | Adds keystore-only mode and throws ClientException on predefined-key decryption mismatch. |
| common/src/main/java/com/microsoft/identity/common/components/AndroidPlatformComponentsFactory.java | Adds factory method to create platform components using keystore-only encryption. |
| common/src/main/java/com/microsoft/identity/deviceregistration/api/DeviceRegistrationClientApplication.kt | Switches Device Registration to keystore-only platform components. |
| common/src/test/java/com/microsoft/identity/common/crypto/AndroidAuthSdkStorageEncryptionManagerTest.java | Updates/extends tests for new exception behavior and keystore-only mode. |
| common/src/test/java/com/microsoft/identity/common/internal/activebrokerdiscovery/BrokerDiscoveryClientTests.kt | Adds E2E test for decryption failure → cache miss → IPC fallback → cache repopulation. |
| common/src/test/java/com/microsoft/identity/common/crypto/MockData.java | Exposes predefined key byte arrays for cross-test usage. |
|
❌ Work item link check failed. Description does not contain AB#{ID}. Click here to Learn more. |
…oidAuthSdkStorageEncryptionManagerTest.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…AndroidPlatformComponentsFactory.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
✅ Work item link check complete. Description contains link AB#3577391 to an Azure Boards work item. |
|
❌ Invalid work item number: AB#3577391 ###. Work item number must be a valid integer. Click here to learn more. |
…xtWithKeystoreOnlyEncryptionForStorage Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/0c880240-e3d1-480e-a646-012e0df956d3 Co-authored-by: rpdome <19558668+rpdome@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # changelog.txt Co-authored-by: rpdome <19558668+rpdome@users.noreply.github.com>
Merged |
AB#3577391
Problem
In Company Portal,
AndroidAuthSdkStorageEncryptionManagerreads from the globalAuthenticationSettings.INSTANCE.getSecretKeyData()singleton. When CP sets a predefined key, WPJ'sDeviceRegistrationClientApplication(which uses that same class as MSAL) inherits it and encrypts the active broker cache with it (U001prefix). If WPJ later runs without the predefined key being set, decryption throwsIllegalStateException— uncaught byEncryptedNameValueStorage.get()(which only catchesClientException) — crashing the app.Specific trigger in Company Portal:
DisableBrokerUseCase(added to the:authprocess in their PR 6333837, July 2022) checks if the device is workplace joined viaIsWorkplaceJoinedUseCase→DeviceRegistrationClientApplication. Since this runs in the:authprocess, the MSAL predefined key is never set, so encryption/decryption of the active broker cache fails.For context: The "predefined" key is a legacy ADAL pattern from when Android Keystore wasn't considered reliable — apps supplied their own encryption key. Company Portal has continued using this approach since then. The predefined key exists to protect token caches;
DeviceRegistrationClientApplicationonly uses encrypted storage for the active broker cache (non-token), so the legacy key isn't needed here. This PR switches it to always use the Android KeyStore instead.Long term wise, we should push Intune/CP to not invoke any APIs on :auth process - but our code should be resilient to how the key is set regardless.
Fix
useKeystoreOnlymode — New constructor param onAndroidAuthSdkStorageEncryptionManager. Whentrue, ignores predefined key fromAuthenticationSettings, always encrypts with Android KeyStore.IllegalStateException→ClientException— Missing predefined key now throwsClientException(DECRYPTION_FAILED), whichEncryptedNameValueStorage.get()catches → returns null → cache miss →BrokerDiscoveryClientfalls back to IPC → read the cached value from one of the broker apps -> self-heals.DeviceRegistrationClientApplicationandBrokerAppsApiwired to keystore-only — via newAndroidPlatformComponentsFactory.createFromContextWithKeystoreOnlyEncryption(). MSAL behavior unchanged.Changed files
AndroidAuthSdkStorageEncryptionManager.java—useKeystoreOnlyparam,ClientExceptioninstead ofIllegalStateExceptionAndroidPlatformComponentsFactory.java— newcreateFromContextWithKeystoreOnlyEncryption()DeviceRegistrationClientApplication.kt(common + broker) — use keystore-only factoryAndroidAuthSdkStorageEncryptionManagerTest.java— tests for new behaviorBrokerDiscoveryClientTests.kt— E2E: predefined-key cache → keystore-only read → IPC fallback → cache re-populated