Skip to content

Commit ccc7fd3

Browse files
authored
Merge branch 'dev' into pedroro/duna-rework
2 parents a56a813 + e29fcf1 commit ccc7fd3

File tree

5 files changed

+311
-2
lines changed

5 files changed

+311
-2
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Add SDMBroadcastReceiver for applications to register callbacks for SDM broadcasts (#2547)
34
- [MINOR] Add switch_browser toMicrosoftStsAuthorizationRequest (#2550)
45
- [MAJOR] Add suberror for network errors (#2537)
56
- [PATCH] Translate MFA token error to UIRequiredException instead of ServiceException (#2538)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal.broker;
24+
25+
import android.content.Intent;
26+
27+
import androidx.test.core.app.ApplicationProvider;
28+
29+
import com.microsoft.identity.common.java.constants.SharedDeviceModeConstants;
30+
31+
import org.junit.Assert;
32+
import org.junit.Before;
33+
import org.junit.Test;
34+
import org.junit.runner.RunWith;
35+
import org.junit.runners.JUnit4;
36+
37+
@RunWith(JUnit4.class)
38+
public class SDMBroadcastReceiverTests {
39+
40+
String actualCallbackReceived;
41+
42+
@Before
43+
public void setup() {
44+
SDMBroadcastReceiver.initialize(ApplicationProvider.getApplicationContext(), new SDMBroadcastReceiver.SharedDeviceModeCallback() {
45+
@Override
46+
public void onSharedDeviceModeRegistrationStarted() {
47+
actualCallbackReceived = SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTRATION_START;
48+
}
49+
50+
@Override
51+
public void onSharedDeviceModeRegistered() {
52+
actualCallbackReceived = SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTERED;
53+
}
54+
55+
@Override
56+
public void onGlobalSignOut() {
57+
actualCallbackReceived = SharedDeviceModeConstants.BROADCAST_TYPE_GLOBAL_SIGN_OUT;
58+
}
59+
});
60+
}
61+
@Test
62+
public void testSDMBroadcast() throws InterruptedException {
63+
sendBroadcast(SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTRATION_START);
64+
Thread.sleep(100);
65+
Assert.assertEquals(SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTRATION_START, actualCallbackReceived);
66+
67+
sendBroadcast(SharedDeviceModeConstants.BROADCAST_TYPE_GLOBAL_SIGN_OUT);
68+
Thread.sleep(100);
69+
Assert.assertEquals(SharedDeviceModeConstants.BROADCAST_TYPE_GLOBAL_SIGN_OUT, actualCallbackReceived);
70+
}
71+
72+
private void sendBroadcast(String broadcastType) {
73+
final Intent intent = new Intent();
74+
intent.setAction(SharedDeviceModeConstants.CURRENT_ACCOUNT_CHANGED_BROADCAST_IDENTIFIER);
75+
intent.putExtra(SharedDeviceModeConstants.BROADCAST_TYPE_KEY, broadcastType);
76+
ApplicationProvider.getApplicationContext().sendBroadcast(intent);
77+
}
78+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.internal.broker;
24+
25+
import static com.microsoft.identity.common.java.cache.SharedPreferencesAccountCredentialCache.DEFAULT_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES;
26+
27+
import android.content.BroadcastReceiver;
28+
import android.content.Context;
29+
import android.content.Intent;
30+
import android.content.IntentFilter;
31+
import android.os.Build;
32+
33+
import androidx.annotation.NonNull;
34+
35+
import com.microsoft.identity.common.components.AndroidPlatformComponentsFactory;
36+
import com.microsoft.identity.common.internal.activebrokerdiscovery.BrokerDiscoveryClientFactory;
37+
import com.microsoft.identity.common.internal.controllers.BrokerMsalController;
38+
import com.microsoft.identity.common.java.cache.CacheKeyValueDelegate;
39+
import com.microsoft.identity.common.java.cache.IAccountCredentialCache;
40+
import com.microsoft.identity.common.java.cache.SharedPreferencesAccountCredentialCache;
41+
import com.microsoft.identity.common.java.commands.parameters.CommandParameters;
42+
import com.microsoft.identity.common.java.constants.SharedDeviceModeConstants;
43+
import com.microsoft.identity.common.java.exception.BaseException;
44+
import com.microsoft.identity.common.java.interfaces.IPlatformComponents;
45+
import com.microsoft.identity.common.logging.Logger;
46+
47+
import java.util.UUID;
48+
49+
/**
50+
* Broadcast receiver listening for Shared device mode broadcasts from broker.
51+
*/
52+
public class SDMBroadcastReceiver {
53+
private static final String TAG = SDMBroadcastReceiver.class.getSimpleName();
54+
private static BroadcastReceiver sSDMBroadcastReceiver;
55+
56+
/**
57+
* Initializes the SDM broadcast receiver to start listening for SDM broadcasts from broker
58+
* @param context application context.
59+
* @param sharedDeviceModeCallback a callback to be called when SDM broadcast is received.
60+
*/
61+
synchronized public static void initialize( @NonNull final Context context,
62+
@NonNull final SharedDeviceModeCallback sharedDeviceModeCallback) {
63+
if (sSDMBroadcastReceiver == null) {
64+
sSDMBroadcastReceiver = new BroadcastReceiver() {
65+
@Override
66+
public void onReceive(final Context context, final Intent intent) {
67+
handleSharedDeviceModeBroadCast(context, intent, sharedDeviceModeCallback);
68+
}
69+
};
70+
71+
final IntentFilter filter = new IntentFilter(SharedDeviceModeConstants.CURRENT_ACCOUNT_CHANGED_BROADCAST_IDENTIFIER);
72+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
73+
context.registerReceiver(sSDMBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
74+
} else {
75+
context.registerReceiver(sSDMBroadcastReceiver, filter);
76+
}
77+
}
78+
}
79+
80+
/**
81+
* Handles the SDM broadcast and calls the callback method based on the broadcast type
82+
* @param context application context.
83+
* @param intent The receive intent for SDM broadcast.
84+
* @param sharedDeviceModeCallback Callback to be called.
85+
*/
86+
private static void handleSharedDeviceModeBroadCast(@NonNull final Context context,
87+
@NonNull final Intent intent,
88+
@NonNull SharedDeviceModeCallback sharedDeviceModeCallback) {
89+
final String methodTag = TAG + ":handleSharedDeviceModeBroadCast";
90+
final String broadcastType = intent.getStringExtra(SharedDeviceModeConstants.BROADCAST_TYPE_KEY);
91+
Logger.info(methodTag, "Received SDM broadcast with type: " + broadcastType);
92+
try {
93+
final IPlatformComponents platformComponents = AndroidPlatformComponentsFactory.createFromContext(context);
94+
if (broadcastType == null) {
95+
Logger.warn(methodTag, "ignoring null broadcast type ");
96+
} else {
97+
switch (broadcastType) {
98+
case SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTRATION_START:
99+
sharedDeviceModeCallback.onSharedDeviceModeRegistrationStarted();
100+
break;
101+
case SharedDeviceModeConstants.BROADCAST_TYPE_SDM_REGISTERED:
102+
if (isDeviceInSharedMode(context, platformComponents)) {
103+
Logger.info(methodTag, "Device is registered in SDM, clearing default account cache.");
104+
final IAccountCredentialCache accountCredentialCache = new SharedPreferencesAccountCredentialCache(
105+
new CacheKeyValueDelegate(),
106+
platformComponents.getStorageSupplier().getEncryptedNameValueStore(
107+
DEFAULT_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES, String.class)
108+
);
109+
accountCredentialCache.clearAll();
110+
sharedDeviceModeCallback.onSharedDeviceModeRegistered();
111+
} else {
112+
Logger.warn(methodTag, "Device not in shared device mode, ignore broadcast.");
113+
}
114+
break;
115+
case SharedDeviceModeConstants.BROADCAST_TYPE_GLOBAL_SIGN_OUT:
116+
sharedDeviceModeCallback.onGlobalSignOut();
117+
break;
118+
default:
119+
Logger.warn(methodTag, "ignoring unknown broadcast type " + broadcastType);
120+
break;
121+
}
122+
}
123+
} catch (final BaseException e) {
124+
Logger.error(methodTag, "Failed to handle SDM broadcast", e);
125+
}
126+
}
127+
128+
private static boolean isDeviceInSharedMode(@NonNull final Context context,
129+
@NonNull IPlatformComponents platformComponents) throws BaseException {
130+
final BrokerData activeBroker = BrokerDiscoveryClientFactory.getInstanceForBrokerSdk(context, platformComponents)
131+
.getActiveBroker(false);
132+
if (activeBroker == null) {
133+
return false;
134+
}
135+
final BrokerMsalController brokerMsalController = new BrokerMsalController(context, platformComponents, activeBroker.getPackageName());
136+
final CommandParameters commandParameters;
137+
commandParameters = CommandParameters.builder()
138+
.platformComponents(platformComponents)
139+
.correlationId(UUID.randomUUID().toString())
140+
.build();
141+
return brokerMsalController.getDeviceMode(commandParameters);
142+
}
143+
144+
/**
145+
* Callback for SDM broadcasts
146+
*/
147+
public interface SharedDeviceModeCallback {
148+
/**
149+
* Called when shared device mode registration is initiated.
150+
*/
151+
void onSharedDeviceModeRegistrationStarted();
152+
153+
/**
154+
* Called when device is registered in shared device mode.
155+
*/
156+
void onSharedDeviceModeRegistered();
157+
158+
/**
159+
* Called when global sign out occurs.
160+
*/
161+
void onGlobalSignOut();
162+
}
163+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424

2525
import android.content.Context;
2626
import android.content.Intent;
27-
import android.os.Parcelable;
2827

2928
import androidx.annotation.Nullable;
3029

3130
import com.microsoft.identity.common.java.util.IBroadcaster;
3231
import com.microsoft.identity.common.java.util.ported.PropertyBag;
32+
import com.microsoft.identity.common.logging.Logger;
3333

3434
import lombok.AllArgsConstructor;
3535
import lombok.NonNull;
@@ -39,17 +39,20 @@
3939
*/
4040
@AllArgsConstructor
4141
public class AndroidBroadcaster implements IBroadcaster {
42+
private static final String TAG = AndroidBroadcaster.class.getSimpleName();
4243

4344
@NonNull
4445
private final Context mContext;
4546

4647
@Override
4748
public void sendBroadcast(@NonNull final String broadcastId, @Nullable final PropertyBag propertyBag) {
49+
final String methodTag = TAG + ":sendBroadcast";
50+
Logger.info(methodTag, "Sending broadcast with broadcastId: " + broadcastId);
4851
final Intent intent = new Intent();
4952
intent.setAction(broadcastId);
5053
if(propertyBag != null) {
5154
for (final String key : propertyBag.keySet()) {
52-
intent.putExtra(key, propertyBag.<Parcelable[]>get(key));
55+
intent.putExtra(key, propertyBag.<String>get(key));
5356
}
5457
}
5558
mContext.sendBroadcast(intent);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
24+
package com.microsoft.identity.common.java.constants
25+
26+
/**
27+
* Constants related to Shared Device Mode
28+
*/
29+
class SharedDeviceModeConstants {
30+
companion object {
31+
/**
32+
* BroadcastId of the Account Change operation.
33+
*/
34+
const val CURRENT_ACCOUNT_CHANGED_BROADCAST_IDENTIFIER = "com.microsoft.identity.client.sharedmode.CURRENT_ACCOUNT_CHANGED"
35+
36+
/**
37+
* Broadcast type key sent as extra in the broadcast intent
38+
*/
39+
const val BROADCAST_TYPE_KEY = "BROADCAST_TYPE"
40+
41+
/**
42+
* Broadcast type for SDM registration start
43+
*/
44+
const val BROADCAST_TYPE_SDM_REGISTRATION_START = "SDM_REGISTRATION_START"
45+
46+
/**
47+
* Broadcast type for SDM registration complete
48+
*/
49+
const val BROADCAST_TYPE_SDM_REGISTERED = "SDM_REGISTERED"
50+
51+
/**
52+
* Broadcast type for SDM GLOBAL_SIGN_OUT
53+
*/
54+
const val BROADCAST_TYPE_GLOBAL_SIGN_OUT = "GLOBAL_SIGN_OUT"
55+
56+
/**
57+
* Prefix for the account name used for the Device Account
58+
* when performing userless shared device registration using preauthorized challenge
59+
* The full account name is this prefix followed by the tenant-id
60+
* where the device is getting registered.
61+
*/
62+
const val DEVICE_WORK_ACCOUNT_FOR_TENANT_PREFIX = "Device Work account for Tenant:"
63+
}
64+
}

0 commit comments

Comments
 (0)