How to handle OAuth URL callbacks with Intent filt

2019-03-11 06:41发布

I am developing an app which uses OAuth for authentication but I have a little problem handling OAuth callbacks.

THE AUTHENTICATION

My app has a webview as the login screen and I am given a url to load the auth form in my webview. Let's say that the url is :

https://myoauthhost.com/oauth/auth?response_type=code&client_id=XXXXXXX&redirect_uri=YYYYYYYY&scope=ZZZZZZZZZZ

and in the auth activity (AuthActivity.java), I have the following :

    String authURL = https://myoauthhost.com/oauth/auth?response_type=code&client_id=XXXXXXX&redirect_uri=YYYYYYYY&scope=ZZZZZZZZZZ
    myWebView.loadUrl(authURL);

in the manifest.xml, I have the following for oauth callback handling :

<activity
            android:name=".AuthActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >

            <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:host="authprovider"
                    android:scheme="auth" />
            </intent-filter>
</activity>

THE PROBLEM

This url when used in the webview (with loadURL() method) redirects to another url containing the REAL OAUTH WEB FROM (that should be loaded in the webview). The problem is that this redirection launches automatically the intent selection in Android : since the URL should be handled by a web browser, Android lets you choose one of the available web browser on the phone to open the url.

Since this is not what I want, I have to include the following code so that the redirection is handled within the webview but does not launch a web browser (or whatever) :

myWebView.setWebViewClient(new WebViewClient());

so with this code, the redirection is handled "within the webview" and I have the login screen displayed.

I can then enter the credentials (e.g : oauth via Twitter) but when the authentication is done, the call back is received but then the activity which is supposed to handle the callback (AuthActivity configured to receive callback in the manifest) is not launched. Instead, I have the webview displaying a message saying that the url callback (in our case : authprovider://auth/XXX?xxx=yyy as configured in the manifest) can not be found.

The reason may be that the following code :

myWebView.setWebViewClient(new WebViewClient());

introduced earlier, tells Android that the webview handles everything. So now, since the callback url is not a web url, it has trouble to handle it and can not even launch the intent which can handle it.

THE QUESTION

How can I solve this problem ? I should be able to let the activity handle the callback but not let the webview try to load it.

any help would be appreciated

thanks in advance

3条回答
倾城 Initia
2楼-- · 2019-03-11 07:18

Well, first you may need call the URL that is provided by your service provider, if there is any redirect exist you will get HTTP status code 3xx. Next you can try to call the actual URL if there is any redirect exist. For normal HTTP response, you will get HTTP 2xx status code.

查看更多
等我变得足够好
3楼-- · 2019-03-11 07:19

As you have the Object of WebView so you can overload it and then handle the url on which you wana navigate to your Activity,in these two way.

private  class MyWebView extends WebViewClient{   
            @TargetApi(Build.VERSION_CODES.N)
            @Override
            public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request)
            {
                Uri uri = request.getUrl();
                return shouldOverrideUrlLoading(uri.toString());
            }

            private boolean shouldOverrideUrlLoading(final String url)
            {
                Log.i(TAG, "shouldOverrideUrlLoading() URL : " + url);
                   //your code to handle the url for navigation will go here
                try{
                      if(url.contains("yourcondition")){
                        //1st way to handle 
                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setData(Uri.parse(url));
                        YourActivity.this.startActivity(intent);
                       }else{
                           //2nd way 
                        Intent intent = new Intent(YourActivity.this,NewActivity.class);
                        //you can set the data 
                        intent.setData(Uri.parse(url));
                        YourActivity.this.startActivity(intent);
                       }
                     }catch(ActivityNotFoundException e) {    
                       Log.e(LOGTAG,"Could not load url"+url);
                    }                    
               return false;
               // return true; // this True means that application wants to leave the WebView and try to go to browser and handle the url itself, otherwise return false.
             } 
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) 
    {


        try {
              if(url.contains("yourcondition")){
                //1st way to handle 
              Intent intent = new Intent(Intent.ACTION_VIEW);
              intent.setData(Uri.parse(url));
              YourActivity.this.startActivity(intent);
            }else{
           //2nd way 
            Intent intent = new Intent(YourActivity.this,NewActivity.class);
           //you can set the data 
            intent.setData(Uri.parse(url));
            YourActivity.this.startActivity(intent);
            }

        }   catch(ActivityNotFoundException e) {

            Log.e(LOGTAG,"Could not load url"+url);
        }

        return false;     
    }
}

you can set the mWebView.setWebViewClient(new WebViewClient()); which is default but by just adding a default custom WebViewClient will only allow the WebView to handle any loaded urls itself i.e. within the webView instead of browser.

but if you overload by passing above class MyWebView like mWebView.setWebViewClient(new MyWebView());then you can control the the loading of url same thing u can achive

myWebView.setWebViewClient(new WebViewClient(){ 

                @TargetApi(Build.VERSION_CODES.N)
                @Override
                public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request)
                {
                    Uri uri = request.getUrl();
                    return shouldOverrideUrlLoading(uri.toString());
                }

                private boolean shouldOverrideUrlLoading(final String url)
                {
                    Log.i(TAG, "shouldOverrideUrlLoading() URL : " + url);
                       //your code to handle the url for navigation will go here
                    try{
                          if(url.contains("yourcondition")){
                            //1st way to handle 
                            Intent intent = new Intent(Intent.ACTION_VIEW);
                            intent.setData(Uri.parse(url));
                            YourActivity.this.startActivity(intent);
                           }else{
                               //2nd way 
                            Intent intent = new Intent(YourActivity.this,NewActivity.class);
                            //you can set the data 
                            intent.setData(Uri.parse(url));
                            YourActivity.this.startActivity(intent);
                           }
                         }catch(ActivityNotFoundException e) {    
                           Log.e(LOGTAG,"Could not load url"+url);
                        }                    
                   return false;
                   // return true; // this True means that application wants to leave the WebView and try to go to browser and handle the url itself, otherwise return false.
                 } 

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) 
    {

        try {
             if(url.contains("yourcondition")){
             //1st way to handle
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(url));
            YourActivity.this.startActivity(intent);
            }else{
             //2nd way 
                Intent intent = new Intent(YourActivity.this,NewActivity.class);
           //you can set the data 
            intent.setData(Uri.parse(url));
            YourActivity.this.startActivity(intent);
           }    
        }   catch(ActivityNotFoundException e) {

            Log.e(LOGTAG,"Could not load url"+url);
        }

        return false;      

    }});

In case you don't have control of webview object then this is not the solution i will update once i will solve this.

查看更多
我命由我不由天
4楼-- · 2019-03-11 07:21

First of all in your manifest, set these properties to your activity that launches the WebView

android:launchMode="singleInstance" 

and add an intent filter to that as

<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="oauth-testing" />
</intent-filter>

then in your code when the user clicks on the login button

mReqToken = mTwitter.getOAuthRequestToken(CALLBACK_URL);
WebView webView = new WebView(this);
webView.requestFocus(View.FOCUS_DOWN);
webView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
            if (!v.hasFocus()) {
                v.requestFocus();
            }
            break;
        }
        return false;
    }
});
webView.loadUrl(mReqToken.getAuthenticationURL());
mainLayout.removeAllViews();
mainLayout.addView(webView);

Here the callback url is
private static final String CALLBACK_URL = "oauth-testing:///";
and you are creating a dynamic webview and displaying to the user. And after logging in the webview is closed and the code comes to the onNewIntent(). You need to implement your functionality after logging in there.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    dealWithTwitterResponse(intent);
}  
private void dealWithTwitterResponse(Intent intent) {
    Uri uri = intent.getData();
    System.out.println("URI=" + uri);
    if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
        String oauthVerifier = uri.getQueryParameter("oauth_verifier");
        authoriseNewUser(oauthVerifier);
    }
}

I know I have added a lot of code snippet, some of which might not be relevant, but i hope it will help someone someday.

查看更多
登录 后发表回答