react-native-msal2 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-msal2",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "license": "MIT",
5
5
  "description": "MSAL React Native wrapper for iOS and Android",
6
6
  "homepage": "https://github.com/bittu/react-native-msal2#readme",
@@ -1,80 +0,0 @@
1
- package com.reactnativemsal;
2
-
3
- import java.io.File;
4
- import java.io.FileOutputStream;
5
- import java.io.IOException;
6
- import java.io.InputStream;
7
- import java.io.OutputStream;
8
-
9
- /**
10
- * Code copied from org.apache.commons.io/FileUtils.java and org.apache.commons.io/IOUtils.java,
11
- * v2.8
12
- */
13
- public class FileUtils {
14
- public static final int DEFAULT_BUFFER_SIZE = 8192;
15
- public static final int EOF = -1;
16
-
17
- public static void copyInputStreamToFile(InputStream source, File destination)
18
- throws IOException {
19
- try (InputStream inputStream = source) {
20
- copyToFile(inputStream, destination);
21
- }
22
- }
23
-
24
- public static void copyToFile(InputStream inputStream, File file) throws IOException {
25
- try (OutputStream out = openOutputStream(file, false)) {
26
- copy(inputStream, out);
27
- }
28
- }
29
-
30
- public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
31
- if (file == null) {
32
- throw new IOException("File cannot be null.");
33
- }
34
- if (file.exists()) {
35
- if (!file.isFile()) {
36
- throw new IOException("Not a file: " + file);
37
- }
38
- if (!file.canWrite()) {
39
- throw new IOException("File is not writable: '" + file + "'");
40
- }
41
- } else {
42
- File parent = file.getParentFile();
43
- if (parent != null) {
44
- if (!parent.mkdirs() && !parent.isDirectory()) {
45
- throw new IOException("Cannot create directory '" + parent + "'.");
46
- }
47
- }
48
- }
49
- return new FileOutputStream(file, append);
50
- }
51
-
52
- public static int copy(InputStream inputStream, OutputStream outputStream) throws IOException {
53
- long count = copyLarge(inputStream, outputStream);
54
- if (count > Integer.MAX_VALUE) {
55
- return EOF;
56
- }
57
- return (int) count;
58
- }
59
-
60
- public static long copyLarge(InputStream inputStream, OutputStream outputStream)
61
- throws IOException {
62
- return copy(inputStream, outputStream, DEFAULT_BUFFER_SIZE);
63
- }
64
-
65
- public static long copy(InputStream inputStream, OutputStream outputStream, int bufferSize)
66
- throws IOException {
67
- return copyLarge(inputStream, outputStream, new byte[bufferSize]);
68
- }
69
-
70
- public static long copyLarge(InputStream inputStream, OutputStream outputStream, byte[] buffer)
71
- throws IOException {
72
- long count = 0;
73
- int n;
74
- while (EOF != (n = inputStream.read(buffer))) {
75
- outputStream.write(buffer, 0, n);
76
- count += n;
77
- }
78
- return count;
79
- }
80
- }
@@ -1,482 +0,0 @@
1
- package com.reactnativemsal;
2
-
3
- import android.content.pm.PackageInfo;
4
- import android.content.pm.PackageManager;
5
- import android.content.pm.Signature;
6
- import android.net.Uri;
7
- import android.util.Base64;
8
- import android.util.Log;
9
- import android.util.Pair;
10
-
11
- import androidx.annotation.NonNull;
12
- import androidx.annotation.Nullable;
13
-
14
- import com.facebook.react.bridge.Arguments;
15
- import com.facebook.react.bridge.Promise;
16
- import com.facebook.react.bridge.ReactApplicationContext;
17
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
18
- import com.facebook.react.bridge.ReactMethod;
19
- import com.facebook.react.bridge.ReadableArray;
20
- import com.facebook.react.bridge.ReadableMap;
21
- import com.facebook.react.bridge.WritableArray;
22
- import com.facebook.react.bridge.WritableMap;
23
- import com.microsoft.identity.client.AcquireTokenParameters;
24
- import com.microsoft.identity.client.AcquireTokenSilentParameters;
25
- import com.microsoft.identity.client.AuthenticationCallback;
26
- import com.microsoft.identity.client.IAccount;
27
- import com.microsoft.identity.client.IAuthenticationResult;
28
- import com.microsoft.identity.client.IMultiTenantAccount;
29
- import com.microsoft.identity.client.IMultipleAccountPublicClientApplication;
30
- import com.microsoft.identity.client.Prompt;
31
- import com.microsoft.identity.client.PublicClientApplication;
32
- import com.microsoft.identity.client.SilentAuthenticationCallback;
33
- import com.microsoft.identity.client.exception.MsalException;
34
-
35
- import org.json.JSONArray;
36
- import org.json.JSONException;
37
- import org.json.JSONObject;
38
-
39
- import java.io.File;
40
- import java.io.FileWriter;
41
- import java.security.MessageDigest;
42
- import java.util.AbstractMap;
43
- import java.util.ArrayList;
44
- import java.util.List;
45
- import java.util.Map;
46
- import java.util.regex.Matcher;
47
- import java.util.regex.Pattern;
48
-
49
- import static com.reactnativemsal.ReadableMapUtils.getStringOrDefault;
50
- import static com.reactnativemsal.ReadableMapUtils.getStringOrThrow;
51
-
52
- public class RNMSALModule extends ReactContextBaseJavaModule {
53
- private static final String AUTHORITY_TYPE_B2C = "B2C";
54
- private static final String AUTHORITY_TYPE_AAD = "AAD";
55
-
56
- private static final Pattern aadAuthorityPattern = Pattern.compile("https://login\\.microsoftonline\\.com/([^/]+)");
57
- private static final Pattern b2cAuthorityPattern = Pattern.compile("https://([^/]+)/([^/]+)/.+");
58
-
59
- private IMultipleAccountPublicClientApplication publicClientApplication;
60
-
61
- public RNMSALModule(ReactApplicationContext reactContext) {
62
- super(reactContext);
63
- }
64
-
65
- @NonNull
66
- @Override
67
- public String getName() {
68
- return "RNMSAL";
69
- }
70
-
71
- @ReactMethod
72
- public void createPublicClientApplication(ReadableMap params, Promise promise) {
73
- ReactApplicationContext context = getReactApplicationContext();
74
- try {
75
- // We have to make a json file containing the MSAL configuration, then use that file to
76
- // create the PublicClientApplication
77
- // We first need to create the JSON model using the passed in parameters
78
-
79
- JSONObject msalConfigJsonObj = params.hasKey("androidConfigOptions")
80
- ? ReadableMapUtils.toJsonObject(params.getMap("androidConfigOptions"))
81
- : new JSONObject();
82
-
83
- // Account mode. Required to be MULTIPLE for this library
84
- msalConfigJsonObj.put("account_mode", "MULTIPLE");
85
-
86
- // If broker_redirect_uri_registered is not provided in androidConfigOptions,
87
- // default it to false
88
- if (!msalConfigJsonObj.has("broker_redirect_uri_registered")) {
89
- msalConfigJsonObj.put("broker_redirect_uri_registered", false);
90
- }
91
-
92
- ReadableMap auth = params.getMap("auth");
93
-
94
- // Authority
95
- String authority = getStringOrDefault(auth, "authority", "https://login.microsoftonline.com/common");
96
- msalConfigJsonObj.put("authority", authority);
97
-
98
- // Client id
99
- msalConfigJsonObj.put("client_id", getStringOrThrow(auth, "clientId"));
100
-
101
- // Redirect URI
102
- msalConfigJsonObj.put("redirect_uri", auth.hasKey("redirectUri") ? auth.getString("redirectUri") : makeRedirectUri(context).toString());
103
-
104
- // Authorities
105
- ReadableArray knownAuthorities = auth.getArray("knownAuthorities");
106
- // List WILL be instantiated and empty if `knownAuthorities` is null
107
- List<String> authoritiesList = readableArrayToStringList(knownAuthorities);
108
- // Make sure the `authority` makes it in the authority list
109
- if (!authoritiesList.contains(authority)) {
110
- authoritiesList.add(authority);
111
- }
112
- // The authoritiesList is just a list of urls (strings), but the native android MSAL
113
- // library expects an array of objects, so we have to parse the urls
114
- JSONArray authoritiesJsonArr = makeAuthoritiesJsonArray(authoritiesList, authority);
115
- msalConfigJsonObj.put("authorities", authoritiesJsonArr);
116
-
117
- // Serialize the JSON config to a string
118
- String serializedMsalConfig = msalConfigJsonObj.toString();
119
- Log.d("RNMSALModule", serializedMsalConfig);
120
-
121
- // Create a temporary file and write the serialized config to it
122
- File file = File.createTempFile("RNMSAL_msal_config", ".tmp");
123
- file.deleteOnExit();
124
- FileWriter writer = new FileWriter(file);
125
- writer.write(serializedMsalConfig);
126
- writer.close();
127
-
128
- // Finally, create the PCA with the temporary config file we created
129
- publicClientApplication =
130
- PublicClientApplication.createMultipleAccountPublicClientApplication(
131
- context, file);
132
- promise.resolve(null);
133
- } catch (Exception e) {
134
- promise.reject(e);
135
- }
136
- }
137
-
138
- private JSONArray makeAuthoritiesJsonArray(List<String> authorityUrls, String authority) throws JSONException, IllegalArgumentException {
139
- JSONArray authoritiesJsonArr = new JSONArray();
140
- boolean foundDefaultAuthority = false;
141
-
142
- for (String authorityUrl : authorityUrls) {
143
- JSONObject authorityJsonObj = new JSONObject();
144
-
145
- // Authority is set as the default if one is not set yet, and it matches `authority`
146
- if (!foundDefaultAuthority && authorityUrl.equals(authority)) {
147
- authorityJsonObj.put("default", true);
148
- foundDefaultAuthority = true;
149
- }
150
-
151
- Matcher aadAuthorityMatcher = aadAuthorityPattern.matcher(authorityUrl);
152
- Matcher b2cAuthorityMatcher = b2cAuthorityPattern.matcher(authorityUrl);
153
-
154
- if (aadAuthorityMatcher.find()) {
155
- String group = aadAuthorityMatcher.group(1);
156
- if (group == null)
157
- throw new IllegalArgumentException("Could not match group 1 for regex https://login.microsoftonline.com/([^/]+) in authority \"" + authorityUrl + "\"");
158
-
159
- JSONObject audience;
160
- switch (group) {
161
- case "common":
162
- audience = new JSONObject().put("type", "AzureADandPersonalMicrosoftAccount");
163
- break;
164
- case "organizations":
165
- audience = new JSONObject().put("type", "AzureADMultipleOrgs");
166
- break;
167
- case "consumers":
168
- audience = new JSONObject().put("type", "PersonalMicrosoftAccount");
169
- break;
170
- default:
171
- // assume `group` is a tenant id
172
- audience = new JSONObject().put("type", "AzureADMyOrg").put("tenant_id", group);
173
- break;
174
- }
175
- authorityJsonObj.put("type", AUTHORITY_TYPE_AAD);
176
- authorityJsonObj.put("audience", audience);
177
- } else if (b2cAuthorityMatcher.find()) {
178
- authorityJsonObj.put("type", AUTHORITY_TYPE_B2C);
179
- authorityJsonObj.put("authority_url", authorityUrl);
180
- } else {
181
- throw new IllegalArgumentException("Authority \"" + authorityUrl + "\" doesn't match AAD regex https://login.microsoftonline.com/([^/]+) or B2C regex https://([^/]+)/tfp/([^/]+)/.+");
182
- }
183
-
184
- authoritiesJsonArr.put(authorityJsonObj);
185
- }
186
-
187
- // If a default authority was not found, we set the first authority as the default
188
- if (!foundDefaultAuthority && authoritiesJsonArr.length() > 0) {
189
- authoritiesJsonArr.getJSONObject(0).put("default", true);
190
- }
191
-
192
- return authoritiesJsonArr;
193
- }
194
-
195
- private Uri makeRedirectUri(ReactApplicationContext context) throws Exception {
196
- try {
197
- final String packageName = context.getPackageName();
198
- final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
199
- if (info.signatures.length != 1) {
200
- throw new RuntimeException("RNMSAL expected there to be exactly one signature for package " + packageName);
201
- }
202
- Signature signature = info.signatures[0];
203
- final MessageDigest messageDigest = MessageDigest.getInstance("SHA");
204
- messageDigest.update(signature.toByteArray());
205
- final String signatureHash = Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP);
206
- Log.d("RNMSALModule", signatureHash);
207
-
208
- return new Uri.Builder().scheme("msauth")
209
- .authority(packageName)
210
- .appendPath(signatureHash)
211
- .build();
212
- } catch (Exception ex) {
213
- throw new Exception("Could not create redirect uri from package name and signature hash", ex);
214
- }
215
- }
216
-
217
- @ReactMethod
218
- public void acquireToken(ReadableMap params, Promise promise) {
219
- try {
220
- AcquireTokenParameters.Builder acquireTokenParameters =
221
- new AcquireTokenParameters.Builder()
222
- .startAuthorizationFromActivity(this.getCurrentActivity());
223
-
224
- // Required parameters
225
- List<String> scopes = readableArrayToStringList(params.getArray("scopes"));
226
- acquireTokenParameters.withScopes(scopes);
227
-
228
- // Optional parameters
229
- if (params.hasKey("authority")) {
230
- acquireTokenParameters.fromAuthority(params.getString("authority"));
231
- }
232
-
233
- if (params.hasKey("promptType")) {
234
- acquireTokenParameters.withPrompt(Prompt.values()[params.getInt("promptType")]);
235
- }
236
-
237
- if (params.hasKey("loginHint")) {
238
- acquireTokenParameters.withLoginHint(params.getString("loginHint"));
239
- }
240
-
241
- if (params.hasKey("extraScopesToConsent")) {
242
- acquireTokenParameters.withOtherScopesToAuthorize(
243
- readableArrayToStringList(params.getArray("extraScopesToConsent")));
244
- }
245
-
246
- if (params.hasKey("extraQueryParameters")) {
247
- List<Map.Entry<String, String>> parameters = new ArrayList<>();
248
- for (Map.Entry<String, Object> entry :
249
- params.getMap("extraQueryParameters").toHashMap().entrySet()) {
250
- parameters.add(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().toString()));
251
- }
252
- acquireTokenParameters.withAuthorizationQueryStringParameters(parameters);
253
- }
254
-
255
- acquireTokenParameters.withCallback(getAuthInteractiveCallback(promise));
256
- publicClientApplication.acquireToken(acquireTokenParameters.build());
257
- } catch (Exception e) {
258
- promise.reject(e);
259
- }
260
- }
261
-
262
- private AuthenticationCallback getAuthInteractiveCallback(Promise promise) {
263
- return new AuthenticationCallback() {
264
- @Override
265
- public void onCancel() {
266
- promise.reject("userCancel", "userCancel");
267
- }
268
-
269
- @Override
270
- public void onSuccess(IAuthenticationResult authenticationResult) {
271
- if (authenticationResult != null) {
272
- promise.resolve(msalResultToDictionary(authenticationResult));
273
- } else {
274
- promise.resolve(null);
275
- }
276
- }
277
-
278
- @Override
279
- public void onError(MsalException exception) {
280
- promise.reject(exception);
281
- }
282
- };
283
- }
284
-
285
- @ReactMethod
286
- public void acquireTokenSilent(ReadableMap params, Promise promise) {
287
- try {
288
- AcquireTokenSilentParameters.Builder acquireTokenSilentParameters =
289
- new AcquireTokenSilentParameters.Builder();
290
-
291
- // Required parameters
292
- List<String> scopes = readableArrayToStringList(params.getArray("scopes"));
293
- acquireTokenSilentParameters.withScopes(scopes);
294
-
295
- ReadableMap accountIn = params.getMap("account");
296
- String accountIdentifier = accountIn.getString("identifier");
297
- IAccount account = publicClientApplication.getAccount(accountIdentifier);
298
- acquireTokenSilentParameters.forAccount(account);
299
-
300
- // Optional parameters
301
- String authority =
302
- publicClientApplication
303
- .getConfiguration()
304
- .getDefaultAuthority()
305
- .getAuthorityURL()
306
- .toString();
307
- if (params.hasKey("authority")) {
308
- authority = params.getString("authority");
309
- }
310
- acquireTokenSilentParameters.fromAuthority(authority);
311
-
312
- if (params.hasKey("forceRefresh")) {
313
- acquireTokenSilentParameters.forceRefresh(params.getBoolean("forceRefresh"));
314
- }
315
-
316
- acquireTokenSilentParameters.withCallback(getAuthSilentCallback(promise));
317
- publicClientApplication.acquireTokenSilentAsync(acquireTokenSilentParameters.build());
318
- } catch (Exception e) {
319
- promise.reject(e);
320
- }
321
- }
322
-
323
- private SilentAuthenticationCallback getAuthSilentCallback(Promise promise) {
324
- return new SilentAuthenticationCallback() {
325
- @Override
326
- public void onSuccess(IAuthenticationResult authenticationResult) {
327
- if (authenticationResult != null) {
328
- promise.resolve(msalResultToDictionary(authenticationResult));
329
- } else {
330
- promise.resolve(null);
331
- }
332
- }
333
-
334
- @Override
335
- public void onError(MsalException exception) {
336
- promise.reject(exception);
337
- }
338
- };
339
- }
340
-
341
- @ReactMethod
342
- public void getAccounts(Promise promise) {
343
- try {
344
- List<IAccount> accounts = publicClientApplication.getAccounts();
345
- WritableArray array = Arguments.createArray();
346
- if (accounts != null) {
347
- for (IAccount account : accounts) {
348
- array.pushMap(accountToMap(account));
349
- }
350
- }
351
- promise.resolve(array);
352
- } catch (Exception e) {
353
- promise.reject(e);
354
- }
355
- }
356
-
357
- @ReactMethod
358
- public void getAccount(String accountIdentifier, Promise promise) {
359
- try {
360
- IAccount account = publicClientApplication.getAccount(accountIdentifier);
361
- if (account != null) {
362
- promise.resolve(accountToMap(account));
363
- } else {
364
- promise.resolve(null);
365
- }
366
- } catch (Exception e) {
367
- promise.reject(e);
368
- }
369
- }
370
-
371
- @ReactMethod
372
- public void removeAccount(ReadableMap accountIn, Promise promise) {
373
- try {
374
- // Required parameters
375
- String accountIdentifier = accountIn.getString(("identifier"));
376
- IAccount account = publicClientApplication.getAccount(accountIdentifier);
377
-
378
- publicClientApplication.removeAccount(
379
- account,
380
- new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
381
- @Override
382
- public void onRemoved() {
383
- promise.resolve(true);
384
- }
385
-
386
- @Override
387
- public void onError(@NonNull MsalException exception) {
388
- promise.reject(exception);
389
- }
390
- });
391
- } catch (Exception e) {
392
- promise.reject(e);
393
- }
394
- }
395
-
396
- private WritableMap msalResultToDictionary(@NonNull IAuthenticationResult result) {
397
- WritableMap map = Arguments.createMap();
398
- map.putString("accessToken", result.getAccessToken());
399
- map.putString("expiresOn", String.format("%s", result.getExpiresOn().getTime() / 1000));
400
- String idToken = result.getAccount().getIdToken();
401
- if (idToken == null) {
402
- idToken = ((IMultiTenantAccount) result.getAccount()).getTenantProfiles().get(result.getTenantId()).getIdToken();
403
- }
404
- map.putString("idToken", idToken);
405
- map.putArray("scopes", Arguments.fromArray(result.getScope()));
406
- map.putString("tenantId", result.getTenantId());
407
- map.putMap("account", accountToMap(result.getAccount()));
408
- return map;
409
- }
410
-
411
- private WritableMap accountToMap(@NonNull IAccount account) {
412
- WritableMap map = Arguments.createMap();
413
- map.putString("identifier", account.getId());
414
- map.putString("username", account.getUsername());
415
- map.putString("tenantId", account.getTenantId());
416
- Map<String, ?> claims = account.getClaims();
417
- if (claims != null) {
418
- map.putMap("claims", toWritableMap(claims));
419
- }
420
- return map;
421
- }
422
-
423
-
424
- @NonNull
425
- private List<String> readableArrayToStringList(@Nullable ReadableArray readableArray) {
426
- List<String> list = new ArrayList<>();
427
- if (readableArray != null) {
428
- for (Object item : readableArray.toArrayList()) {
429
- list.add(item.toString());
430
- }
431
- }
432
- return list;
433
- }
434
-
435
- @NonNull
436
- private WritableMap toWritableMap(@NonNull Map<String, ?> map) {
437
- WritableMap writableMap = Arguments.createMap();
438
- for (Map.Entry<String, ?> entry : map.entrySet()) {
439
- String key = entry.getKey();
440
- Object value = entry.getValue();
441
- if (value == null) {
442
- writableMap.putNull(key);
443
- } else if (value instanceof Boolean) {
444
- writableMap.putBoolean(key, (Boolean) value);
445
- } else if (value instanceof Double) {
446
- writableMap.putDouble(key, (Double) value);
447
- } else if (value instanceof Integer) {
448
- writableMap.putInt(key, (Integer) value);
449
- } else if (value instanceof String) {
450
- writableMap.putString(key, (String) value);
451
- } else if (value instanceof Map<?, ?>) {
452
- writableMap.putMap(key, toWritableMap((Map<String, ?>) value));
453
- } else if (value instanceof List<?>) {
454
- writableMap.putArray(key, toWritableArray((List<?>) value));
455
- }
456
- }
457
- return writableMap;
458
- }
459
-
460
- @NonNull
461
- private WritableArray toWritableArray(@NonNull List<?> list) {
462
- WritableArray writableArray = Arguments.createArray();
463
- for (Object value : list.toArray()) {
464
- if (value == null) {
465
- writableArray.pushNull();
466
- } else if (value instanceof Boolean) {
467
- writableArray.pushBoolean((Boolean) value);
468
- } else if (value instanceof Double) {
469
- writableArray.pushDouble((Double) value);
470
- } else if (value instanceof Integer) {
471
- writableArray.pushInt((Integer) value);
472
- } else if (value instanceof String) {
473
- writableArray.pushString((String) value);
474
- } else if (value instanceof Map<?, ?>) {
475
- writableArray.pushMap(toWritableMap((Map<String, ?>) value));
476
- } else if (value instanceof List<?>) {
477
- writableArray.pushArray(toWritableArray((List<?>) value));
478
- }
479
- }
480
- return writableArray;
481
- }
482
- }
@@ -1,25 +0,0 @@
1
- package com.reactnativemsal;
2
-
3
- import androidx.annotation.NonNull;
4
-
5
- import com.facebook.react.ReactPackage;
6
- import com.facebook.react.bridge.NativeModule;
7
- import com.facebook.react.bridge.ReactApplicationContext;
8
- import com.facebook.react.uimanager.ViewManager;
9
-
10
- import java.util.Collections;
11
- import java.util.List;
12
-
13
- public class RNMSALPackage implements ReactPackage {
14
- @NonNull
15
- @Override
16
- public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
17
- return Collections.singletonList(new RNMSALModule(reactContext));
18
- }
19
-
20
- @NonNull
21
- @Override
22
- public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
23
- return Collections.emptyList();
24
- }
25
- }