androidandroid-intentdeep-linkingappauth

AppAuth-Android strange problem in handling NOT hierarchical URI callback intent


I am using AppAuth-Android in my app to handle the OAuth flow. When the flow starts, I call the startActivityForResults method as described in the https://github.com/openid/AppAuth-Android#obtaining-an-authorization-code step. After calling this method, the browser pops up waiting for the user login.

In my manifest I have

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:scheme="com.app.name"
        android:host="oauth2redirect/login-callback" />
</intent-filter>

that is needed for handling the redirect callback when login step is done, as specified in the AppAuth documentation.

During a penetration test on my app, came to light that if in this particular moment (when browser for authentication is visible), you follow the steps below, the app crash

  1. switch app (e.g. I open a browser, not closing my app)
  2. click on a NOT hierarchical link, com.name.app:abcd in my case (this URL was ready-made by the tester)

This is the stack trace of the crash.

E/AndroidRuntime: FATAL EXCEPTION: main
Process: io.app.mobile, PID: 29226
java.lang.RuntimeException: Unable to resume activity {com.app.name/net.openid.appauth.AuthorizationManagementActivity}: java.lang.UnsupportedOperationException: This isn't a hierarchical URI.
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3830)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3862)
    at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:51)
    at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6854)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)
 Caused by: java.lang.UnsupportedOperationException: This isn't a hierarchical URI.
    at android.net.Uri.getQueryParameterNames(Uri.java:1590)
    at net.openid.appauth.AuthorizationManagementActivity.extractResponseData(AuthorizationManagementActivity.java:318)
    at net.openid.appauth.AuthorizationManagementActivity.handleAuthorizationComplete(AuthorizationManagementActivity.java:259)
    at net.openid.appauth.AuthorizationManagementActivity.onResume(AuthorizationManagementActivity.java:234)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1413)
    at android.app.Activity.performResume(Activity.java:7307)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3822)

The problem seems to be that opening com.name.app:abcd, makes the app believing that there is a result from the intent and net.openid.appauth.AuthorizationManagementActivity tries to handle it

private Intent extractResponseData(Uri responseUri) {
    if (responseUri.getQueryParameterNames().contains(AuthorizationException.PARAM_ERROR)) {
        return AuthorizationException.fromOAuthRedirect(responseUri).toIntent();
    } 

(...)
}

but the getQueryParameterNames of the class java.net.URI throws an exception if the URL is NOT hierarchical.

I am not sure if it is a problem related to the library itself, or if there is a solution that can be accomplished (maybe changing the manifest deep linking handling). The fact is that I do not know how to fix this.

If the question seems too synthetic and you need some additional information to reproduce the problem, feel free to ask for more details. Thanks :)


Solution

  • I assume the intent-filter you posted is set for RedirectUriReceiverActivity, but you can actually have your own Activity instead to be the man-in-the-middle. If you look into the source code it basically does nothing more than

    startActivity(AuthorizationManagementActivity.createResponseHandlingIntent(
                        this, getIntent().getData()));
    
    1. Create your own Activity e.g. AllUriReceiverActivity
    2. set intent-filter (that you posted above) for this AllUriReceiverActivity
    3. in onCreate() of AllUriReceiverActivity add check if getIntent().getData() contains hierarchical URI before performing startActivity() call.

    4. Reuse library's RedirectUriReceiverActivity, so you don't need to worry when library updates the way of instantiating AuthorizationManagementActivity

    So in onCreate() it should be sth like this:

    Uri uri = getIntent().getData();
    
    if (uri.isHierarchical()) {
       Intent i = new Intent(context, RedirectUriReceiverActivity.class);
       i.setData(uri);
       startActivity(i);
    }
    finish();
    

    EDIT: Additionally to get rid of the handling your URI by RedirectUriReceiverActivity, you can remove completely this part suggested by AppAuth lib:

    android.defaultConfig.manifestPlaceholders = [
      'appAuthRedirectScheme': '...'
    ]