diff --git a/docs/docs/usage/config.md b/docs/docs/usage/config.md index 48ff6183..27932168 100644 --- a/docs/docs/usage/config.md +++ b/docs/docs/usage/config.md @@ -33,6 +33,7 @@ See specific example [configurations for your provider](/docs/category/providers - **clientSecret** - (`string`) client secret to pass to token exchange requests. :warning: Read more about [client secrets](/docs/client-secrets) - **redirectUrl** - (`string`) _REQUIRED_ the url that links back to your app with the auth code. Depending on your [provider](/docs/category/providers), you may find that you need to add a trailing slash to your redirect URL. - **scopes** - (`array`) the scopes for your token, e.g. `['email', 'offline_access']`. +- **claims** - (`object`) the requested claims for your token, e.g. ` { "id_token": { "given_name": null } }`. - **additionalParameters** - (`object`) additional parameters that will be passed in the authorization request. Must be string values! E.g. setting `additionalParameters: { hello: 'world', foo: 'bar' }` would add `hello=world&foo=bar` to the authorization request. diff --git a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java index e68389a4..dcee5105 100644 --- a/packages/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java +++ b/packages/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java @@ -57,6 +57,8 @@ import net.openid.appauth.connectivity.ConnectionBuilder; import net.openid.appauth.connectivity.DefaultConnectionBuilder; +import org.json.JSONException; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -98,6 +100,7 @@ public void prefetchConfiguration( final String redirectUrl, final String clientId, final ReadableArray scopes, + final ReadableMap claims, final ReadableMap serviceConfiguration, final boolean dangerouslyAllowInsecureHttpRequests, final ReadableMap customHeaders, @@ -232,6 +235,7 @@ public void authorize( final String clientId, final String clientSecret, final ReadableArray scopes, + final ReadableMap claims, final ReadableMap additionalParameters, final ReadableMap serviceConfiguration, final Boolean skipCodeExchange, @@ -273,6 +277,7 @@ public void authorize( appAuthConfiguration, clientId, scopes, + claims, redirectUrl, useNonce, usePKCE, @@ -304,6 +309,7 @@ public void onFetchConfigurationCompleted( appAuthConfiguration, clientId, scopes, + claims, redirectUrl, useNonce, usePKCE, @@ -654,11 +660,12 @@ private void authorizeWithConfiguration( final AppAuthConfiguration appAuthConfiguration, final String clientId, final ReadableArray scopes, + final ReadableMap claims, final String redirectUrl, final Boolean useNonce, final Boolean usePKCE, final Map additionalParametersMap, - final Boolean androidTrustedWebActivity) { + final Boolean androidTrustedWebActivity) throws Exception { String scopesString = null; @@ -679,6 +686,14 @@ private void authorizeWithConfiguration( authRequestBuilder.setScope(scopesString); } + if (claims != null) { + try { + authRequestBuilder.setClaims(MapUtil.convertMapToJson(claims)); + } catch (JSONException ignored) { + throw new Exception("claims passed but contains invalid JSON"); + } + } + if (additionalParametersMap != null) { // handle additional parameters separately to avoid exceptions from AppAuth if (additionalParametersMap.containsKey("display")) { 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 624201ad..b8f06daa 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 @@ -7,6 +7,8 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; @@ -119,4 +121,59 @@ private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSON } return array; } + + public static JSONObject convertMapToJson(ReadableMap readableMap) throws JSONException { + JSONObject object = new JSONObject(); + ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + switch (readableMap.getType(key)) { + case Null: + object.put(key, JSONObject.NULL); + break; + case Boolean: + object.put(key, readableMap.getBoolean(key)); + break; + case Number: + object.put(key, readableMap.getDouble(key)); + break; + case String: + object.put(key, readableMap.getString(key)); + break; + case Map: + object.put(key, convertMapToJson(readableMap.getMap(key))); + break; + case Array: + object.put(key, convertArrayToJson(readableMap.getArray(key))); + break; + } + } + return object; + } + + private static JSONArray convertArrayToJson(ReadableArray readableArray) throws JSONException { + JSONArray array = new JSONArray(); + for (int i = 0; i < readableArray.size(); i++) { + switch (readableArray.getType(i)) { + case Null: + break; + case Boolean: + array.put(readableArray.getBoolean(i)); + break; + case Number: + array.put(readableArray.getDouble(i)); + break; + case String: + array.put(readableArray.getString(i)); + break; + case Map: + array.put(convertMapToJson(readableArray.getMap(i))); + break; + case Array: + array.put(convertArrayToJson(readableArray.getArray(i))); + break; + } + } + return array; + } } diff --git a/packages/react-native-app-auth/index.d.ts b/packages/react-native-app-auth/index.d.ts index 0c59626f..4b20b6b1 100644 --- a/packages/react-native-app-auth/index.d.ts +++ b/packages/react-native-app-auth/index.d.ts @@ -68,6 +68,7 @@ export type BaseAuthConfiguration = BaseConfiguration & { export type AuthConfiguration = BaseAuthConfiguration & { clientSecret?: string; scopes: string[]; + claims?: object, redirectUrl: string; additionalParameters?: BuiltInParameters & { [name: string]: string }; clientAuthMethod?: 'basic' | 'post'; diff --git a/packages/react-native-app-auth/index.js b/packages/react-native-app-auth/index.js index 7b8f4a4a..5f015c0f 100644 --- a/packages/react-native-app-auth/index.js +++ b/packages/react-native-app-auth/index.js @@ -100,6 +100,7 @@ export const prefetchConfiguration = async ({ redirectUrl, clientId, scopes, + claims, serviceConfiguration, dangerouslyAllowInsecureHttpRequests = false, customHeaders, @@ -118,6 +119,7 @@ export const prefetchConfiguration = async ({ redirectUrl, clientId, scopes, + claims, serviceConfiguration, dangerouslyAllowInsecureHttpRequests, customHeaders, @@ -200,6 +202,7 @@ export const authorize = ({ clientId, clientSecret, scopes, + claims, useNonce = true, usePKCE = true, additionalParameters, @@ -229,6 +232,7 @@ export const authorize = ({ clientId, clientSecret, scopes, + claims, additionalParameters, serviceConfiguration, skipCodeExchange,