Skip to content

[Android] additionalExchangeParameters cannot be null #564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sarahjaoun opened this issue Sep 30, 2020 · 7 comments · Fixed by #942
Closed

[Android] additionalExchangeParameters cannot be null #564

sarahjaoun opened this issue Sep 30, 2020 · 7 comments · Fixed by #942

Comments

@sarahjaoun
Copy link

Issue

Once I upgraded to the 5.1.2 version of the library, I started getting this crash on Android which wasn't the case before.
Firebase Crashlytics is showing that some android users are getting this crash and I am unable to replicate it.

Fatal Exception: java.lang.RuntimeException
Failure delivering result ResultInfo{who=null, request=0, result=-1, data=Intent { dat=com.saicomembers.portal:/callback?code=42c3e7fd5e18f675781670544e6cb7785d0e9e51ccac355a32f2d724476dc64f&scope=openid profile MobileApi offline_access&state=iiVkPbyNcTUftf_WkEWNgQ&session_state=aPQ4ArXSgr71BLZMM4YX_vX38tiI1qgRvlJ55Xe5pkM.b3709100ebfbecb216cd454f2d33b64f (has extras) }} to activity {com.saicohealth.members/com.saicohealth.members.MainActivity}: java.lang.NullPointerException: additionalExchangeParameters cannot be null

Below is the full stack trace:
net.openid.appauth.Preconditions.checkNotNull (Preconditions.java:55)
net.openid.appauth.AuthorizationResponse.createTokenExchangeRequest (AuthorizationResponse.java:454)
com.rnappauth.RNAppAuthModule.onActivityResult (RNAppAuthModule.java:401)
com.facebook.react.bridge.ReactContext.onActivityResult (ReactContext.java:262)
com.facebook.react.ReactInstanceManager.onActivityResult (ReactInstanceManager.java:703)
com.facebook.react.ReactActivityDelegate.onActivityResult (ReactActivityDelegate.java:124)
com.facebook.react.ReactActivity.onActivityResult (ReactActivity.java:75)
android.app.Activity.dispatchActivityResult (Activity.java:8393)
android.app.ActivityThread.deliverResults (ActivityThread.java:5442)
android.app.ActivityThread.handleSendResult (ActivityThread.java:5490)
android.app.servertransaction.ActivityResultItem.execute (ActivityResultItem.java:51)
android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:149)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:103)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2373)
android.os.Handler.dispatchMessage (Handler.java:107)
android.os.Looper.loop (Looper.java:213)
android.app.ActivityThread.main (ActivityThread.java:8147)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:513)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1101)

These are my authentication settings:
const commonConfig = {
clientId: 'NativeApp',
redirectUrl: 'com.saicomembers.portal:/callback',
scopes: ['openid', 'profile', 'MobileApi', 'offline_access'],
serviceConfiguration: {
authorizationEndpoint: API.BASE_URL_IDENTITY + 'connect/authorize?prompt=login&domain=' + API.ORIGIN,
tokenEndpoint: API.BASE_URL_IDENTITY + 'connect/token?domain=' + API.ORIGIN,
}
};
const authenticationConfig = isAndroid() ? {...commonConfig, additionalParameters: {prompt: "login"}} : {...commonConfig, additionalParameters: {prompt: "login", domain: API.ORIGIN}};


Environment

  • IdentityServer 4
  • Android
  • Are you using Expo? No
@lundn
Copy link

lundn commented Jan 16, 2021

We’re seeing the same thing with v5.1.3.

@lordkiz
Copy link

lordkiz commented Mar 2, 2021

Still seeing this error on v6.0.0
This is due to this:

TokenRequest tokenRequest = response.createTokenExchangeRequest(this.additionalParametersMap);
which calls an AuthorizationResponse method createTokenExchangeRequest with additionalParams which should not be null. (see here: https://github.com/Azure-Samples/active-directory-b2c-android-native-appauth/blob/3fee05d1ed6301dd5a9be90f2900c6266c969402/library/java/net/openid/appauth/AuthorizationResponse.java#L458)
I can't figure out why this is happening. Would a workaround be to patch react-native-app-auth to call response. createTokenExchangeRequest() instead when this.additionalParametersMap is null?

@yberstad
Copy link

Hi!

Thanks for your suggestion @lordkiz, it seems to work fine.

This is my patch file which also contains a fix for the
'boolean java.lang.Boolean.booleanValue()' on a null object reference

The main thing is to check for null on skipCodeExchange, usePKCE and additionalParametersMap. I have also wrapped the whole onActivityResult in a try/catch with a custom AppAuthErrorCode, but this is not necessary.

diff --git a/node_modules/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java b/node_modules/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java
index b775886..ff7ebbf 100644
--- a/node_modules/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java
+++ b/node_modules/react-native-app-auth/android/src/main/java/com/rnappauth/RNAppAuthModule.java
@@ -479,95 +479,110 @@ public class RNAppAuthModule extends ReactContextBaseJavaModule implements Activ
      */
     @Override
     public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
-        if (requestCode == 52) {
-            if (data == null) {
-                if (promise != null) {
-                    promise.reject("authentication_error", "Data intent is null" );
+        try {
+            if (requestCode == 52) {
+                if (data == null) {
+                    if (promise != null) {
+                        promise.reject("authentication_error", "Data intent is null" );
+                    }
+                    return;
                 }
-                return;
-            }

-            final AuthorizationResponse response = AuthorizationResponse.fromIntent(data);
-            AuthorizationException ex = AuthorizationException.fromIntent(data);
-            if (ex != null) {
-                if (promise != null) {
-                    handleAuthorizationException("authentication_error", ex, promise);
+                final AuthorizationResponse response = AuthorizationResponse.fromIntent(data);
+                AuthorizationException ex = AuthorizationException.fromIntent(data);
+                if (ex != null) {
+                    if (promise != null) {
+                        handleAuthorizationException("authentication_error", ex, promise);
+                    }
+                    return;
                 }
-                return;
-            }

-            if (this.skipCodeExchange) {
-                WritableMap map;
-                if (this.usePKCE && this.codeVerifier != null) {
-                    map = TokenResponseFactory.authorizationCodeResponseToMap(response, this.codeVerifier);
-                } else {
-                    map = TokenResponseFactory.authorizationResponseToMap(response);
-                }
+                if (this.skipCodeExchange != null && this.skipCodeExchange) {
+                    WritableMap map;
+                    if (this.usePKCE != null && this.usePKCE && this.codeVerifier != null) {
+                        map = TokenResponseFactory.authorizationCodeResponseToMap(response, this.codeVerifier);
+                    } else {
+                        map = TokenResponseFactory.authorizationResponseToMap(response);
+                    }

-                if (promise != null) {
-                    promise.resolve(map);
+                    if (promise != null) {
+                        promise.resolve(map);
+                    }
+                    return;
                 }
-                return;
-            }


-            final Promise authorizePromise = this.promise;
-            final AppAuthConfiguration configuration = createAppAuthConfiguration(
-                    createConnectionBuilder(this.dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders),
-                    this.dangerouslyAllowInsecureHttpRequests,
-                    null
-            );
+                final Promise authorizePromise = this.promise;
+                final AppAuthConfiguration configuration = createAppAuthConfiguration(
+                        createConnectionBuilder(this.dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders),
+                        this.dangerouslyAllowInsecureHttpRequests,
+                        null
+                );

-            AuthorizationService authService = new AuthorizationService(this.reactContext, configuration);
+                AuthorizationService authService = new AuthorizationService(this.reactContext, configuration);

-            TokenRequest tokenRequest = response.createTokenExchangeRequest(this.additionalParametersMap);

-            AuthorizationService.TokenResponseCallback tokenResponseCallback = new AuthorizationService.TokenResponseCallback() {
+                TokenRequest tokenRequest;
+                if (this.additionalParametersMap == null) {
+                    tokenRequest = response.createTokenExchangeRequest();
+                } else {
+                    tokenRequest = response.createTokenExchangeRequest(this.additionalParametersMap);
+                }
+
+                AuthorizationService.TokenResponseCallback tokenResponseCallback = new AuthorizationService.TokenResponseCallback() {

-                @Override
-                public void onTokenRequestCompleted(
-                        TokenResponse resp, AuthorizationException ex) {
-                    if (resp != null) {
-                        WritableMap map = TokenResponseFactory.tokenResponseToMap(resp, response);
-                        if (authorizePromise != null) {
-                            authorizePromise.resolve(map);
-                        }
-                    } else {
-                        if (promise != null) {
-                            handleAuthorizationException("token_exchange_failed", ex, promise);
+                    @Override
+                    public void onTokenRequestCompleted(
+                            TokenResponse resp, AuthorizationException ex) {
+                        if (resp != null) {
+                            WritableMap map = TokenResponseFactory.tokenResponseToMap(resp, response);
+                            if (authorizePromise != null) {
+                                authorizePromise.resolve(map);
+                            }
+                        } else {
+                            if (promise != null) {
+                                handleAuthorizationException("token_exchange_failed", ex, promise);
+                            }
                         }
                     }
-                }
-            };
+                };

-            if (this.clientSecret != null) {
-                ClientAuthentication clientAuth = this.getClientAuthentication(this.clientSecret, this.clientAuthMethod);
-                authService.performTokenRequest(tokenRequest, clientAuth, tokenResponseCallback);
+                if (this.clientSecret != null) {
+                    ClientAuthentication clientAuth = this.getClientAuthentication(this.clientSecret, this.clientAuthMethod);
+                    authService.performTokenRequest(tokenRequest, clientAuth, tokenResponseCallback);

-            } else {
-                authService.performTokenRequest(tokenRequest, tokenResponseCallback);
-            }
+                } else {
+                    authService.performTokenRequest(tokenRequest, tokenResponseCallback);
+                }

-        }
+            }

-        if (requestCode == 53) {
-            if (data == null) {
-                if (promise != null) {
-                    promise.reject("end_session_failed", "Data intent is null" );
+            if (requestCode == 53) {
+                if (data == null) {
+                    if (promise != null) {
+                        promise.reject("end_session_failed", "Data intent is null" );
+                    }
+                    return;
                 }
-                return;
-            }
-            EndSessionResponse response = EndSessionResponse.fromIntent(data);
-            AuthorizationException ex = AuthorizationException.fromIntent(data);
-            if (ex != null) {
-                if (promise != null) {
-                    handleAuthorizationException("end_session_failed", ex, promise);
+                EndSessionResponse response = EndSessionResponse.fromIntent(data);
+                AuthorizationException ex = AuthorizationException.fromIntent(data);
+                if (ex != null) {
+                    if (promise != null) {
+                        handleAuthorizationException("end_session_failed", ex, promise);
+                    }
+                    return;
                 }
-                return;
+                final Promise endSessionPromise = this.promise;
+                WritableMap map = EndSessionResponseFactory.endSessionResponseToMap(response);
+                endSessionPromise.resolve(map);
+            }
+        } catch (Exception e) {
+            if (promise != null) {
+                promise.reject("run_time_exception", e.getMessage());
+            }
+            else {
+                throw e;
             }
-            final Promise endSessionPromise = this.promise;
-            WritableMap map = EndSessionResponseFactory.endSessionResponseToMap(response);
-            endSessionPromise.resolve(map);
         }
     }

diff --git a/node_modules/react-native-app-auth/index.d.ts b/node_modules/react-native-app-auth/index.d.ts
index aaa5c5e..0d92858 100644
--- a/node_modules/react-native-app-auth/index.d.ts
+++ b/node_modules/react-native-app-auth/index.d.ts
@@ -187,7 +187,8 @@ type AppAuthErrorCode =
   | 'registration_failed'
   | 'browser_not_found'
   | 'end_session_failed'
-  | 'authentication_error';
+  | 'authentication_error'
+  | 'run_time_exception';

 type ErrorCode =
   | OAuthAuthorizationErrorCode

@eithe
Copy link

eithe commented Feb 2, 2024

@yberstad Do you happen to have a patch that patches 7.1.0 cleanly?

Edit: Forget it, the above patch works on 7.1.0 as well. It was just an error on my side (tried to apply this patch when it was already patched with the slightly different patch from Nirodha26#1)

@carbonrobot
Copy link
Contributor

@mmalaguti
Copy link

mmalaguti commented Mar 7, 2025

@carbonrobot if this was fixed in v7.1.1 does it mean that it is included in the code moving forward? asking because I was using the latest version and I encountered this error.

@carbonrobot
Copy link
Contributor

@mmalaguti That's correct. If your still having a problem, please file a new issue with reproduction so we can track it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants