Add BrowserRedirectValidator and integrate into AndroidAuthorizationStrategy, Fixes AB#3568776#3070
Add BrowserRedirectValidator and integrate into AndroidAuthorizationStrategy, Fixes AB#3568776#3070
Conversation
|
✅ Work item link check complete. Description contains link AB#3568776 to an Azure Boards work item. |
…Factory - Add MULTIPLE_APPS_LISTENING_CUSTOM_URL_SCHEME constant to ErrorStrings.java (common4j) - Create BrowserRedirectValidator.kt: validates no other app listens on the same custom URL scheme used for BrowserTabActivity redirect URI - Modify AuthorizationActivityFactory.getAuthorizationFragmentFromStartIntent() to accept optional Context and call the validator before browser fragment creation - Update callers (AuthorizationActivity, CurrentTaskAuthorizationActivity, AndroidAuthorizationStrategy) to pass context for validation - Add BrowserRedirectValidatorTest.kt with comprehensive unit tests - Extend AuthorizationActivityFactoryTest.java with validator integration tests Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/5ea3f2f4-472e-4f76-8658-091921409a78 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
…ize, add null redirectUri warning log, extract test constants Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/5ea3f2f4-472e-4f76-8658-091921409a78 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
…ity path, not in Activity.onCreate() The ClientException thrown by BrowserRedirectValidator was unhandled when validation ran inside AuthorizationActivity/CurrentTaskAuthorizationActivity.onCreate(), since Android framework lifecycle methods have no throws clause. The exception would crash the Activity with no proper MsalClientException conversion. Fix: move the validation for the activity-based flow into AndroidAuthorizationStrategy.launchIntent(), where 'throws ClientException' is declared and the exception can propagate correctly through the command pipeline to MSAL's adapter layer (where it becomes MsalClientException). - Fragment path (fragment != null): validation still runs inside the factory call (context is passed from launchIntent()), exception propagates through launchIntent() ✓ - Activity path (fragment == null): validation now runs directly in launchIntent() BEFORE startActivity(), exception propagates through launchIntent() ✓ - AuthorizationActivity.onCreate() / CurrentTaskAuthorizationActivity.onCreate(): no longer pass context to the factory, so no unhandled ClientException risk there Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/df3b75d4-7602-47da-948a-b3421cf0cf2b Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
…on in launchIntent() - AuthorizationActivityFactory.kt: remove context param and validation logic entirely. Factory is now a pure fragment-creation utility with no optional parameters that could allow callers to inadvertently skip the check. - AndroidAuthorizationStrategy.launchIntent(): performs all validation upfront for both fragment and activity paths, WebView flows explicitly excluded. Adds warning logs for null context or null redirectUri instead of silently skipping. - AuthorizationActivityFactoryTest.java: remove validator-specific tests (now covered by BrowserRedirectValidatorTest and new AndroidAuthorizationStrategyValidationTest). Simplify factory tests to pure fragment-type assertions. - AndroidAuthorizationStrategyValidationTest.kt (new): 4 tests covering browser-conflict throws, WebView skips, null-agent-treated-as-browser, and no-conflict pass. Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/a7303d99-19bd-4ab2-9221-cd24067b2c11 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…ityFactory Both were leftover from the intermediate state where context was passed to the factory. Now that all validation lives in AndroidAuthorizationStrategy.launchIntent(), these are dead code. The only remaining change to this file is the Javadoc note explaining why validation is performed upstream. Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/dc9a6656-00d7-44f8-99e0-02c29adaa4b4 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
Yes, the package visibility is the key. To reproduce on API 30+: Option 1 (recommended): Add the Azure sample's package name to the MSAL test app's <queries>
<package android:name="com.azure.sample" /> <!-- or whatever the Azure sample package is -->
...
</queries>Once the target app is visible, Option 2: Test on a device/emulator running API < 30 — package visibility restrictions don't apply there, so all installed apps are visible without any The |
Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/2513a645-158b-4b13-948c-ec8baa6cd44b Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
|
@copilot update the Description, to note that the check previously happened during PCA initialization, but will now happen during the acquire token call. This means that the tradeoff for enabling brokered scenarios without changing authorization_user_agent is that we don't catch the error as early. |
…_BROWSABLE categories Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/ab52f137-a1e0-4463-b18a-5ba7b3a81fbd Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
Updated the PR description with the tradeoff note: the check previously ran during PCA initialization; it now runs during |
|
❌ Invalid work item number: AB#3568776. Work item number must be a valid integer. Click here to learn more. |
|
@copilot there are compilation errors in AndroidAuthorizationStrategyValidationTest |
…ation override Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/706f8c0c-5395-4e0f-b69d-a5bb91dbb130 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/6f1c3fba-6bab-43fc-bb3b-90dce1a49232 Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
|
@copilot still getting compilation errors for OAuth2Strategy and AuthorizationRequest declarations in TestAndroidAuthorizationStrategy. Seems there is some expected type arguements |
…tionTest Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-common-for-android/sessions/ed56863f-7a2c-4ea2-a142-104894e31c9e Co-authored-by: fadidurah <88730756+fadidurah@users.noreply.github.com>
Fixed in 50c4672. |
Multiple-apps-listening-on-same-URL-scheme validation previously ran at PCA initialization time in MSAL, surfacing errors immediately at startup before any token request was made. This caused false positives in broker flows where the browser path is never taken. This PR moves the check to browser authorization launch time (inside
acquireToken) so it only fires when the browser authorization path is actually exercised.Timing tradeoff: Because the check no longer runs at PCA init, redirect URI conflicts are now caught later — only when the user initiates an interactive browser-based sign-in. Apps that use broker without explicitly setting
authorization_user_agenttoBROWSERwill no longer see false-positive errors at startup, but the tradeoff is that genuine URI conflicts will be detected duringacquireTokenrather than at initialization.Changes
common4j— ErrorStringsMULTIPLE_APPS_LISTENING_CUSTOM_URL_SCHEME = "multiple_apps_listening_url_scheme"constantcommon— BrowserRedirectValidator.kt (new)Kotlin
objectwithvalidateNoMultipleAppsListening(context, redirectUri, useCurrentTask):ACTION_VIEWintent withDEFAULT/BROWSABLEcategories and the redirect URI as dataPackageManager.queryIntentActivities(intent, GET_RESOLVED_FILTER)for all matching handlersBrowserTabActivity(orCurrentTaskBrowserTabActivitywhenuseCurrentTask=true)ClientException(MULTIPLE_APPS_LISTENING_CUSTOM_URL_SCHEME, ...)on any other matching activityPackageManageris nullcommon— AuthorizationActivityFactory.ktcontextparameter — no caller can inadvertently skip the checkcommon— AndroidAuthorizationStrategy.javalaunchIntent()— the single point wherethrows ClientExceptionis declared — covering both the fragment-embedded and full-Activity paths with a single upfront checknullauthorizationAgent is treated as a browser flow (default behavior)Exception propagation to MsalClientException
ClientExceptionthrown by the validator flows:launchIntent()(throws ClientException) →requestAuthorization()→acquireToken()→InteractiveTokenCommand.execute()→CommandDispatcher.executeCommand()(caught asBaseException) → MSAL's callback adapter →MsalClientException.Tests
BrowserRedirectValidatorTest.kt(new): 9 unit tests covering pass/fail/edge cases including nullPackageManager, empty resolved list,useCurrentTaskflag selecting the correct activity class, and competing-app detection. Test intent registration mirrors the validator's intent shape (CATEGORY_DEFAULT+CATEGORY_BROWSABLE) to ensure Robolectric shadow PM resolution matches the real query.AuthorizationActivityFactoryTest.java: Tests confirming the factory returns the correct fragment type for WebView and browser agents (validation is not the factory's concern); duplicate tests removed.AndroidAuthorizationStrategyValidationTest.kt(new): 4 tests verifying that browser flows with a competing app throwClientExceptionfromlaunchIntent(), WebView flows are skipped, null agent is treated as browser, and no-conflict flows pass. Competing-app registration uses the correct intent shape (CATEGORY_DEFAULT+CATEGORY_BROWSABLE) to match the validator's query.AB#3568776