Skip to content

Commit 4458ad5

Browse files
authored
Edge TB: changes related to GetToken, Fixes AB#3317102 (#2803)
### Summary Broker PR: AzureAD/ad-accounts-for-android#3267 Changes related to WebApps getToken are done in this PR, and it also notably contains refactoring (such as moving some files from broker to common) and some additions to core classes such as AccountChooser and AccountChooserActivity. To optimize the number of IPCs and repeated code, the getToken operation will be completed as follows: 1. Caller calls BrokerMsalController.ExecuteWebAppsRequest. 2. BrokerMsalController takes the request and calls the broker, which will start the ExecuteWebAppsRequestOperation. 3. ExecuteWebAppsRequestOperation takes a peek at the method and delegates to the correct sub operation. 4. For the WebAppsGetTokenSubOperation, determine if silent is possible. Otherwise, force an interactive request and send an intent back to BrokerMsalController. 5. If silent is possible, try a silent request. If a token is returned, this will get formed into a response and sent back to BrokerMsalController. If an exception is thrown, the operation determines if an interactive request should be made. If true, an intent is sent back. If false, the exception is formed into a response and sent back to BrokerMsalController. 6. Back at BrokerMsalController, if a token response or error response is received, this response string is returned as-is. If an intent is received, BrokerMsalController will start the intent (you'll see some refactoring with the existing acquireToken method to help reusing logic). 7. AccountChooserActivity will handle the request and knows that it is from a webapp. It will return either a token response or an error response in the protocol format. 8. BrokerMsalController returns this response as-is. [AB#3317102](https://identitydivision.visualstudio.com/Engineering/_workitems/edit/3317102)
1 parent 4b60d35 commit 4458ad5

File tree

37 files changed

+1668
-125
lines changed

37 files changed

+1668
-125
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ vNext
88
- [MINOR] Enable Broker Discovery by default in MSAL/Broker API (#2818)
99
- [MINOR] Fix for SDL violation in device pop scenarios, Fixes AB#3284510 (#2744)
1010
- [MINOR] Adds Authentication Constants to be used for broker latency timestamp in response (#2831)
11+
- [MINOR] Add support for WebApps getToken API (#2803)
1112

1213
Version 23.1.1
1314
-----------

common/src/main/java/com/microsoft/identity/common/adal/internal/AuthenticationConstants.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,22 +1379,37 @@ public static String computeMaxHostBrokerProtocol() {
13791379
/**
13801380
* String for broker webapps get contracts result.
13811381
*/
1382-
public static final String BROKER_WEBAPPS_GET_CONTRACTS_RESULT = "contracts";
1382+
public static final String BROKER_WEBAPPS_GET_CONTRACTS_RESULT = "web_apps_contracts";
13831383

13841384
/**
1385-
* String for broker webapps error result.
1385+
* String for broker webapps request.
13861386
*/
1387-
public static final String BROKER_WEB_APPS_ERROR = "error";
1387+
public static final String BROKER_WEB_APPS_EXECUTE_REQUEST = "web_apps_execute_request";
13881388

13891389
/**
1390-
* String for broker webapps request.
1390+
* String for broker webapps additional required params.
13911391
*/
1392-
public static final String BROKER_WEB_APPS_REQUEST = "request";
1392+
public static final String BROKER_WEB_APPS_ADDITIONAL_REQUIRED_PARAMS = "additional_required_params";
13931393

13941394
/**
13951395
* String for broker webapps response.
13961396
*/
1397-
public static final String BROKER_WEB_APPS_RESPONSE = "response";
1397+
public static final String BROKER_WEB_APPS_SUCCESSFUL_RESULT = "web_app_successful_result";
1398+
1399+
/**
1400+
* String for compressed broker webapps response.
1401+
*/
1402+
public static final String BROKER_WEB_APPS_SUCCESSFUL_RESULT_COMPRESSED = "web_app_successful_result_compressed";
1403+
1404+
/**
1405+
* String for broker webapps interactive intent.
1406+
*/
1407+
public static final String BROKER_WEB_APPS_INTERACTIVE_INTENT = "web_apps_interactive_intent";
1408+
1409+
/**
1410+
* String for broker webapps error result.
1411+
*/
1412+
public static final String BROKER_WEB_APPS_ERROR_RESULT = "web_apps_error_result";
13981413

13991414
/**
14001415
* String for generate shr result.
@@ -2142,4 +2157,3 @@ public static final class SdkPlatformFields {
21422157
public static final String VERSION = com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION;
21432158
}
21442159
}
2145-

common/src/main/java/com/microsoft/identity/common/internal/broker/BrokerRequest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ private static final class SerializedNames {
8484
final static String SIGN_IN_WITH_GOOGLE_CREDENTIAL = "sign_in_with_google_credential";
8585

8686
final static String TENANT_ID = "tenant_id";
87+
final static String REQUEST_TYPE = "request_type";
88+
final static String WEB_APPS_STATE = "web_apps_state";
8789
}
8890

8991
/**
@@ -281,4 +283,15 @@ private static final class SerializedNames {
281283
@Nullable
282284
@SerializedName(SerializedNames.TENANT_ID)
283285
private String mTenantId;
286+
287+
@Nullable
288+
@SerializedName(SerializedNames.REQUEST_TYPE)
289+
private String mRequestType;
290+
291+
/**
292+
* State for web apps requests. Make sure not to log this.
293+
*/
294+
@Nullable
295+
@SerializedName(SerializedNames.WEB_APPS_STATE)
296+
private String mWebAppsState;
284297
}

common/src/main/java/com/microsoft/identity/common/internal/cache/WebAppsAccountIdRegistry.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class WebAppsAccountIdRegistry private constructor(
5050
* @param supplier The storage supplier.
5151
* @return A new instance of [WebAppsAccountIdRegistry].
5252
*/
53+
@JvmStatic
5354
fun create(supplier: IStorageSupplier): WebAppsAccountIdRegistry {
5455
val store = supplier.getEncryptedFileStore(WEBAPPS_ACCOUNT_ID_REGISTRY_STORAGE_KEY)
5556
return WebAppsAccountIdRegistry(store)

common/src/main/java/com/microsoft/identity/common/internal/controllers/BrokerMsalController.java

Lines changed: 109 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
// THE SOFTWARE.
2323
package com.microsoft.identity.common.internal.controllers;
2424

25+
import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.Broker.BROKER_WEB_APPS_INTERACTIVE_INTENT;
2526
import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.Broker.CLIENT_ADVERTISED_MAXIMUM_BP_VERSION_KEY;
2627
import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.Broker.CLIENT_CONFIGURED_MINIMUM_BP_VERSION_KEY;
2728
import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.Broker.CLIENT_MAX_PROTOCOL_VERSION;
@@ -66,6 +67,7 @@
6667
import com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle;
6768
import com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy;
6869
import com.microsoft.identity.common.internal.broker.ipc.WebAppsAdditionalRequiredParameters;
70+
import com.microsoft.identity.common.internal.util.WebAppsUtil;
6971
import com.microsoft.identity.common.internal.cache.ActiveBrokerCacheUpdater;
7072
import com.microsoft.identity.common.internal.cache.ClientActiveBrokerCache;
7173
import com.microsoft.identity.common.internal.cache.HelloCache;
@@ -113,6 +115,7 @@
113115
import com.microsoft.identity.common.java.result.GenerateShrResult;
114116
import com.microsoft.identity.common.java.ui.PreferredAuthMethod;
115117
import com.microsoft.identity.common.java.util.BrokerProtocolVersionUtil;
118+
import com.microsoft.identity.common.java.util.ObjectMapper;
116119
import com.microsoft.identity.common.java.util.ResultFuture;
117120
import com.microsoft.identity.common.java.util.StringUtil;
118121
import com.microsoft.identity.common.java.util.ThreadUtils;
@@ -121,8 +124,6 @@
121124
import com.microsoft.identity.common.logging.Logger;
122125
import com.microsoft.identity.common.sharedwithoneauth.OneAuthSharedFunctions;
123126

124-
import java.util.ArrayList;
125-
import java.util.Collections;
126127
import java.util.List;
127128
import java.util.concurrent.ExecutionException;
128129
import java.util.concurrent.TimeUnit;
@@ -350,22 +351,53 @@ private String tryGetNegotiatedProtocolVersionFromHelloCache(
350351
@Override
351352
public AcquireTokenResult acquireToken(final @NonNull InteractiveTokenCommandParameters parameters)
352353
throws BaseException, InterruptedException, ExecutionException {
353-
final String methodTag = TAG + ":acquireToken";
354+
final AcquireTokenResult result;
355+
try {
356+
//Get the broker interactive parameters intent
357+
final Intent interactiveRequestIntent = getBrokerAuthorizationIntent(parameters);
358+
final Bundle resultBundle = acquireTokenInternal(parameters, interactiveRequestIntent);
359+
final String negotiatedBrokerProtocolVersion = resultBundle.getString(NEGOTIATED_BP_VERSION_KEY);
360+
// For MSA Accounts Broker doesn't save the accounts, instead it just passes the result along,
361+
// MSAL needs to save this account locally for future token calls.
362+
// parameters.getOAuth2TokenCache() will be non-null only in case of MSAL native
363+
// If the request is from MSALCPP , OAuth2TokenCache will be null.
364+
if (parameters.getOAuth2TokenCache() != null && !BrokerProtocolVersionUtil.canSupportMsaAccountsInBroker(negotiatedBrokerProtocolVersion)) {
365+
saveMsaAccountToCache(resultBundle, (MsalOAuth2TokenCache) parameters.getOAuth2TokenCache());
366+
}
367+
368+
verifyBrokerVersionIsSupported(resultBundle, parameters.getRequiredBrokerProtocolVersion());
369+
result = mResultAdapter.getAcquireTokenResultFromResultBundle(resultBundle);
370+
} catch (final BaseException | ExecutionException e) {
371+
Telemetry.emit(
372+
new ApiEndEvent()
373+
.putException(e)
374+
.putApiId(TelemetryEventStrings.Api.BROKER_ACQUIRE_TOKEN_INTERACTIVE)
375+
);
376+
throw e;
377+
}
354378

379+
Telemetry.emit(
380+
new ApiEndEvent()
381+
.putResult(result)
382+
.putApiId(TelemetryEventStrings.Api.BROKER_ACQUIRE_TOKEN_INTERACTIVE)
383+
);
384+
385+
return result;
386+
}
387+
388+
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
389+
protected Bundle acquireTokenInternal(final @Nullable InteractiveTokenCommandParameters parameters, final @NonNull Intent interactiveRequestIntent) throws BaseException, InterruptedException, ExecutionException {
390+
final String methodTag = TAG + ":acquireTokenInternal";
355391
Telemetry.emit(
356392
new ApiStartEvent()
357393
.putProperties(parameters)
358394
.putApiId(TelemetryEventStrings.Api.BROKER_ACQUIRE_TOKEN_INTERACTIVE)
359395
);
360-
361-
//Create BrokerResultFuture to block on response from the broker... response will be return as an activity result
396+
//Create BrokerResultFuture to block on response from the broker...
362397
//BrokerActivity will receive the result and ask the API dispatcher to complete the request
363398
//In completeAcquireToken below we will set the result on the future and unblock the flow.
364399
mBrokerResultFuture = new ResultFuture<>();
365400

366-
//Get the broker interactive parameters intent
367-
final Intent interactiveRequestIntent = getBrokerAuthorizationIntent(parameters);
368-
369401
Activity activity = null;
370402
if (parameters instanceof AndroidInteractiveTokenCommandParameters) {
371403
activity = ((AndroidInteractiveTokenCommandParameters) parameters).getActivity();
@@ -418,39 +450,10 @@ public void onReceive(@NonNull PropertyBag propertyBag) {
418450
// Start the BrokerActivity using our existing Activity
419451
activity.startActivity(brokerActivityIntent);
420452
}
421-
422-
final AcquireTokenResult result;
423-
try {
424-
//Wait to be notified of the result being returned... we could add a timeout here if we want to
425-
final Bundle resultBundle = mBrokerResultFuture.get();
426-
427-
final String negotiatedBrokerProtocolVersion = interactiveRequestIntent.getStringExtra(NEGOTIATED_BP_VERSION_KEY);
428-
// For MSA Accounts Broker doesn't save the accounts, instead it just passes the result along,
429-
// MSAL needs to save this account locally for future token calls.
430-
// parameters.getOAuth2TokenCache() will be non-null only in case of MSAL native
431-
// If the request is from MSALCPP , OAuth2TokenCache will be null.
432-
if (parameters.getOAuth2TokenCache() != null && !BrokerProtocolVersionUtil.canSupportMsaAccountsInBroker(negotiatedBrokerProtocolVersion)) {
433-
saveMsaAccountToCache(resultBundle, (MsalOAuth2TokenCache) parameters.getOAuth2TokenCache());
434-
}
435-
436-
verifyBrokerVersionIsSupported(resultBundle, parameters.getRequiredBrokerProtocolVersion());
437-
result = mResultAdapter.getAcquireTokenResultFromResultBundle(resultBundle);
438-
} catch (final BaseException | ExecutionException e) {
439-
Telemetry.emit(
440-
new ApiEndEvent()
441-
.putException(e)
442-
.putApiId(TelemetryEventStrings.Api.BROKER_ACQUIRE_TOKEN_INTERACTIVE)
443-
);
444-
throw e;
445-
}
446-
447-
Telemetry.emit(
448-
new ApiEndEvent()
449-
.putResult(result)
450-
.putApiId(TelemetryEventStrings.Api.BROKER_ACQUIRE_TOKEN_INTERACTIVE)
451-
);
452-
453-
return result;
453+
//Wait to be notified of the result being returned... we could add a timeout here if we want to
454+
final Bundle resultBundle = mBrokerResultFuture.get();
455+
resultBundle.putString(NEGOTIATED_BP_VERSION_KEY, interactiveRequestIntent.getStringExtra(NEGOTIATED_BP_VERSION_KEY));
456+
return resultBundle;
454457
}
455458

456459
@Override
@@ -1425,60 +1428,85 @@ public void putValueInSuccessEvent(@NonNull final ApiEndEvent event,
14251428
/**
14261429
* Execute web app request in broker.
14271430
*
1428-
* @param request request string
1431+
* @param request request string.
14291432
* @param minBrokerProtocolVersion minimum broker protocol version the caller requires.
14301433
* @param additionalRequiredParams additional required parameters for web app request.
14311434
* @throws BaseException
14321435
*/
14331436
public String executeWebAppRequest(@NonNull final String request,
14341437
@NonNull final String minBrokerProtocolVersion,
14351438
@NonNull final WebAppsAdditionalRequiredParameters additionalRequiredParams) throws BaseException {
1436-
return getBrokerOperationExecutor().execute(null,
1437-
new BrokerOperation<String>() {
1438-
private String negotiatedBrokerProtocolVersion;
1439+
try {
1440+
return getBrokerOperationExecutor().execute(null,
1441+
new BrokerOperation<String>() {
1442+
private String negotiatedBrokerProtocolVersion;
14391443

1440-
@Override
1441-
public void performPrerequisites(@NonNull final IIpcStrategy strategy) throws BaseException {
1442-
negotiatedBrokerProtocolVersion = hello(strategy, minBrokerProtocolVersion);
1443-
}
1444+
@Override
1445+
public void performPrerequisites(@NonNull final IIpcStrategy strategy) throws BaseException {
1446+
negotiatedBrokerProtocolVersion = hello(strategy, minBrokerProtocolVersion);
1447+
}
14441448

1445-
@NonNull
1446-
@Override
1447-
public BrokerOperationBundle getBundle() throws ClientException {
1448-
return new BrokerOperationBundle(
1449-
BrokerOperationBundle.Operation.BROKER_WEBAPPS_API_EXECUTE_WEB_APPS_REQUEST,
1450-
mActiveBrokerPackageName,
1451-
mRequestAdapter.getRequestBundleForExecuteWebAppRequest(request,negotiatedBrokerProtocolVersion, minBrokerProtocolVersion)
1452-
);
1453-
}
1449+
@NonNull
1450+
@Override
1451+
public BrokerOperationBundle getBundle() throws ClientException {
1452+
final String additionalParamsString = ObjectMapper.serializeObjectToJsonString(additionalRequiredParams);
1453+
return new BrokerOperationBundle(
1454+
BrokerOperationBundle.Operation.BROKER_WEBAPPS_API_EXECUTE_WEB_APPS_REQUEST,
1455+
mActiveBrokerPackageName,
1456+
mRequestAdapter.getRequestBundleForExecuteWebAppRequest(
1457+
request,
1458+
negotiatedBrokerProtocolVersion,
1459+
minBrokerProtocolVersion,
1460+
additionalParamsString
1461+
)
1462+
);
1463+
}
14541464

1455-
@NonNull
1456-
@Override
1457-
public String extractResultBundle(@Nullable final Bundle resultBundle) throws BaseException {
1458-
if (resultBundle == null) {
1459-
throw mResultAdapter.getExceptionForEmptyResultBundle();
1465+
@NonNull
1466+
@Override
1467+
public String extractResultBundle(@Nullable final Bundle resultBundle) throws BaseException {
1468+
if (resultBundle == null) {
1469+
throw mResultAdapter.getExceptionForEmptyResultBundle();
1470+
}
1471+
verifyBrokerVersionIsSupported(resultBundle, minBrokerProtocolVersion);
1472+
if (resultBundle.containsKey(BROKER_WEB_APPS_INTERACTIVE_INTENT)) {
1473+
final Intent interactiveIntent = resultBundle.getParcelable(BROKER_WEB_APPS_INTERACTIVE_INTENT);
1474+
if (interactiveIntent != null) {
1475+
try {
1476+
final Bundle interactiveGetTokenBundle = acquireTokenInternal(null, interactiveIntent);
1477+
return mResultAdapter.getExecuteWebAppRequestResultFromBundle(interactiveGetTokenBundle);
1478+
} catch (final Throwable t) {
1479+
return WebAppsUtil.createErrorResponseString(t, "Error occurred during interactive request process");
1480+
}
1481+
}
1482+
}
1483+
return mResultAdapter.getExecuteWebAppRequestResultFromBundle(resultBundle);
14601484
}
1461-
verifyBrokerVersionIsSupported(resultBundle, minBrokerProtocolVersion);
1462-
return mResultAdapter.getExecuteWebAppRequestResultFromBundle(resultBundle);
1463-
}
14641485

1465-
@NonNull
1466-
@Override
1467-
public String getMethodName() {
1468-
return ":executeWebAppRequest";
1469-
}
1486+
@NonNull
1487+
@Override
1488+
public String getMethodName() {
1489+
return ":executeWebAppRequest";
1490+
}
14701491

1471-
@Nullable
1472-
@Override
1473-
public String getTelemetryApiId() {
1474-
return null;
1475-
}
1492+
@Nullable
1493+
@Override
1494+
public String getTelemetryApiId() {
1495+
return null;
1496+
}
14761497

1477-
@Override
1478-
public void putValueInSuccessEvent(@NonNull final ApiEndEvent event,
1479-
@NonNull final String result) {
1480-
}
1481-
});
1498+
@Override
1499+
public void putValueInSuccessEvent(@NonNull final ApiEndEvent event,
1500+
@NonNull final String result) {
1501+
}
1502+
});
1503+
} catch (final UnsupportedBrokerException ex) {
1504+
// We want to throw this exception to keep it in line with the other APIs.
1505+
throw ex;
1506+
}
1507+
catch (final Exception ex) {
1508+
return WebAppsUtil.createErrorResponseString(ex, "Unexpected error occurred");
1509+
}
14821510
}
14831511

14841512
/**

common/src/main/java/com/microsoft/identity/common/internal/platform/AndroidPlatformUtil.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.microsoft.identity.common.java.commands.ICommand;
5151
import com.microsoft.identity.common.java.commands.InteractiveTokenCommand;
5252
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;
53+
import com.microsoft.identity.common.java.exception.ArgumentException;
5354
import com.microsoft.identity.common.java.exception.ClientException;
5455
import com.microsoft.identity.common.java.exception.ErrorStrings;
5556
import com.microsoft.identity.common.java.flighting.CommonFlight;
@@ -161,6 +162,11 @@ public boolean isValidCallingApp(@NonNull String redirectUri, @NonNull String pa
161162
return isValidBrokerRedirect;
162163
}
163164

165+
@Override
166+
public void isValidCallingAppForWebApps(int callingUid) throws ClientException, UnsupportedOperationException {
167+
// This operation is not supported in non-broker contexts.
168+
throw new UnsupportedOperationException("WebApp APIs are not functional in non-broker scenarios.");
169+
}
164170
@Override
165171
@Nullable
166172
public String getEnrollmentId(@NonNull final String userId, @NonNull final String packageName) {
@@ -324,4 +330,8 @@ private boolean isValidHubRedirectURIForNAATests(String redirectUri) {
324330
|| redirectUri.equals("msauth://com.microsoft.teams/fcg80qvoM1YMKJZibjBwQcDfOno=")
325331
|| redirectUri.equals("https://login.microsoftonline.com/common/oauth2/nativeclient"));
326332
}
333+
334+
protected Context getContext() {
335+
return mContext;
336+
}
327337
}

0 commit comments

Comments
 (0)