diff --git a/.changeset/appauth-android-response-values.md b/.changeset/appauth-android-response-values.md new file mode 100644 index 000000000..c3f68165f --- /dev/null +++ b/.changeset/appauth-android-response-values.md @@ -0,0 +1,5 @@ +--- +"react-native-app-auth": patch +--- + +Decode JSON objects and arrays once, preserve nested null values, accept null native values without a crash, and retain scalar/malformed strings. Iterate map entries directly and share the base authorization response serializer when adding a PKCE verifier. diff --git a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/MapUtil.java b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/MapUtil.java index 624201ad5..4efa65301 100644 --- a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/MapUtil.java +++ b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/MapUtil.java @@ -15,6 +15,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.json.JSONTokener; import java.util.HashMap; import java.util.Iterator; @@ -39,24 +40,27 @@ public static HashMap readableMapToHashMap(@Nullable ReadableMap public static final WritableMap createAdditionalParametersMap(Map additionalParameters) { WritableMap additionalParametersMap = Arguments.createMap(); - if (!additionalParameters.isEmpty()) { - - Iterator iterator = additionalParameters.keySet().iterator(); - - while(iterator.hasNext()) { - String key = iterator.next(); - String value = additionalParameters.get(key); - // Try to parse to JSON + for (Map.Entry entry : additionalParameters.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (value != null) { try { - JSONObject jsonObject = new JSONObject(value); - WritableMap json = convertJsonToMap(jsonObject); - additionalParametersMap.putMap(key, json); - continue; + JSONTokener tokener = new JSONTokener(value); + Object parsed = tokener.nextValue(); + if (tokener.nextClean() == 0) { + if (parsed instanceof JSONObject) { + additionalParametersMap.putMap(key, convertJsonToMap((JSONObject) parsed)); + continue; + } + if (parsed instanceof JSONArray) { + additionalParametersMap.putArray(key, convertJsonToArray((JSONArray) parsed)); + continue; + } + } } catch (JSONException ignored) { - } - additionalParametersMap.putString(key, additionalParameters.get(key)); } + additionalParametersMap.putString(key, value); } return additionalParametersMap; @@ -69,7 +73,9 @@ private static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONEx while (iterator.hasNext()) { String key = iterator.next(); Object value = jsonObject.get(key); - if (value instanceof JSONObject) { + if (value == JSONObject.NULL) { + map.putNull(key); + } else if (value instanceof JSONObject) { map.putMap(key, convertJsonToMap((JSONObject) value)); } else if (value instanceof JSONArray) { map.putArray(key, convertJsonToArray((JSONArray) value)); @@ -97,7 +103,9 @@ private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSON for (int i = 0; i < jsonArray.length(); i++) { Object value = jsonArray.get(i); - if (value instanceof JSONObject) { + if (value == JSONObject.NULL) { + array.pushNull(); + } else if (value instanceof JSONObject) { array.pushMap(convertJsonToMap((JSONObject) value)); } else if (value instanceof JSONArray) { array.pushArray(convertJsonToArray((JSONArray) value)); diff --git a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/TokenResponseFactory.java b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/TokenResponseFactory.java index 38467ebaa..0b336009a 100644 --- a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/TokenResponseFactory.java +++ b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/utils/TokenResponseFactory.java @@ -91,17 +91,7 @@ public static final WritableMap authorizationResponseToMap(AuthorizationResponse * Read raw authorization into a React Native map with codeVerifier value added if present to be passed down the bridge */ public static final WritableMap authorizationCodeResponseToMap(AuthorizationResponse authResponse, String codeVerifier) { - WritableMap map = Arguments.createMap(); - map.putString("authorizationCode", authResponse.authorizationCode); - map.putString("accessToken", authResponse.accessToken); - map.putMap("additionalParameters", MapUtil.createAdditionalParametersMap(authResponse.additionalParameters)); - map.putString("idToken", authResponse.idToken); - map.putString("tokenType", authResponse.tokenType); - map.putArray("scopes", createScopeArray(authResponse.scope)); - - if (authResponse.accessTokenExpirationTime != null) { - map.putString("accessTokenExpirationTime", DateUtil.formatTimestamp(authResponse.accessTokenExpirationTime)); - } + WritableMap map = authorizationResponseToMap(authResponse); if (!TextUtils.isEmpty(codeVerifier)) { map.putString("codeVerifier", codeVerifier);