Android Dev - Callback URL not working... (0_o)
Asked Answered
L

1

6

I'm working on an android application for my research, and I am working with OAuth (signpost library) to gain access to user data from a web service which is also a part of the development process. I am able to flow through the common steps of OAuth, and I use a Uri (for callback to the app), and can get to the step where I invoke the devices browser, choose to verify my app, and the next step is SUPPOSED to redirect the browser BACK to the application....

Instead I get an error that reads something like "you do not have permission to open:

appSchema://appName?authorizationSensitiveInfo..." the appendages after the '?' are the oauth_token and oauth_verifier from the service (we can assume all steps up until the redirection are "correct").

Possible problems lie within the appSchema://appName part. from my understanding this is the redirect URL which tells the Uri to use the phone's browser to locate my application and invoke the onResume() method. Where do the values for appSchema://appName come from (defined in manifest? if so where?).

Why the problem with permission? Must I set permissions for my Uri to access my app? I'm lost...if you need code snippets to help me please reply, I didn't include any code because this is more like a concept I just missed...I'm not at my machine now but I can supply code if that would make things easier to understand. Really beating my head in here...

IN RESPONE TO A GREAT ANSWER HERE IS HOW I HANDLE MY ON RESUME

protected void onResume() {
    super.onResume();       
    Uri uri = this.getIntent().getData();
    if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
        Log.d("StepGreenM", uri.toString());
        String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
        Log.d("StepGreenM", verifier);
        try {

            provider.retrieveAccessToken(consumer, verifier);
            TOKEN = consumer.getToken();
            REQUEST_SECRET = consumer.getTokenSecret();

            Log.d("StepGreenM", TOKEN);
            Log.d("StepGreenM", REQUEST_SECRET);

        } catch (OAuthMessageSignerException e) {
            e.printStackTrace();
        } catch (OAuthNotAuthorizedException e) {
            e.printStackTrace();
        } catch (OAuthExpectationFailedException e) {
            e.printStackTrace();
        } catch (OAuthCommunicationException e) {
            e.printStackTrace();
        }
    }

    uri = getIntent().getData();
    if (uri != null && CALLBACK_URI.getScheme().equals(uri.getScheme())) {
        String token = settings.getString(HomeScreen.REQUEST_TOKEN, null);
        String secret = settings.getString(HomeScreen.REQUEST_SECRET, null);
        Intent i = new Intent(Intent.ACTION_VIEW); // Intent to go to the action view

        try {
            if(!(token == null || secret == null)) {
                consumer.setTokenWithSecret(token, secret);
            }
            String otoken = uri.getQueryParameter(OAuth.OAUTH_TOKEN);
            String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);

            // We send out and save the request token, but the secret is not the same as the verifier
            // Apparently, the verifier is decoded to get the secret, which is then compared - crafty
            // This is a sanity check which should never fail - hence the assertion
            Assert.assertEquals(otoken, consumer.getToken());

            // This is the moment of truth - we could throw here
            provider.retrieveAccessToken(consumer, verifier);
            // Now we can retrieve the goodies
            token = consumer.getToken();
            secret = consumer.getTokenSecret();
            //Save it to a settings file
            HomeScreen.saveAuthInformation(settings, token, secret);
            // Clear the request stuff, now that we have the real thing
            HomeScreen.saveRequestInformation(settings, null, null);
            i.putExtra(USER_TOKEN, token);
            i.putExtra(CONSUMER_SECRET, secret);

            //GO TO APPLICATION

        } catch (OAuthMessageSignerException e) {
            e.printStackTrace();
        } catch (OAuthNotAuthorizedException e) {
            e.printStackTrace();
        } catch (OAuthExpectationFailedException e) {
            e.printStackTrace();
        } catch (OAuthCommunicationException e) {
            e.printStackTrace();
        } finally {
            startActivity(i); // we either authenticated and have the extras or not, but are going to the action view
            this.setContentView(R.layout.indivaction);
            finish();
        }
    }
}

Not sure what really makes this fall apart...but like I said it force closes when this method is invoked. I know it makes it past the redirect because I use an httpSniffer to check the messages to and from the server...

Lollipop answered 5/4, 2011 at 0:21 Comment(0)
R
11

In order for the callback uri to work properly you need to add an intent filter similar to the following to your manifest in the activity you want to use it in.

   <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="appSchema" android:host="appName"/> 
   </intent-filter>

Now, if your activity is using singleInstance/singleTask, you should use something similar to the following:

@Override
public void onNewIntent(Intent intent) {

    super.onNewIntent(intent);
    Uri uri = intent.getData();
    String oauthToken = uri.getQueryParameter("oauth_token");
    String oauthVerifier = uri.getQueryParameter("oauth_verifier");

    //...do what you need with the parameters
}

if not using singleTask or singleInstance, you can do

@Override
public void onResume() {

    super.onResume();
    Intent intent = getIntent();
    Uri uri = intent.getData();
    String oauthToken = uri.getQueryParameter("oauth_token");
    String oauthVerifier = uri.getQueryParameter("oauth_verifier");

    //...do what you need with the parameters
}

I believe this should work.

Also, if I'm not mistaken, the callback url you provide should include the ?, so "appSchema://appName?"

Robers answered 5/4, 2011 at 0:42 Comment(10)
I'll give this a try and let you know if that works. I had that manifest intent filter jazz in my main activity, I'll move it to the activity which uses Oauth and append the '?' to my callback URL. Thank you!Lollipop
Are you launching the browser via an intent of type ACTION_VIEW? One possible annoyance you may run into here is that although the browser will redirect back to your app, it will not auto-close itself. Not sure if that would hinder what you are trying to accomplish.Robers
I am using ACTION_VIEW, and one question I have is how do I determine if i'm using a singleinstance/singleTop, is it as simple as if I do not see those words (i sure never invoke either manually) then i can use onResume as opposed to onNewIntent(). I included the intent-filter in the activity in the manifest, but now my app force closes. What I am attempting to do upon resume is save the oauth info from the callback to a SharedPreferences file so that other activities of my app can use that. but rather than the permission message, it just force closes :(Lollipop
Ah my bad, I meant singleInstance/singleTask, but if you haven't set android:launchMode for that activity in your manifest, it should be using standard, which means you are correct in using onResume as opposed to onNewIntent. When you finish with the browser, does the callback re-invoke your activity?Robers
So your activity force closes after the redirect back from the browser? If that's the case, I would check logcat, it's possible the culprit is some exception you are not catching.Robers
thanks for all of your help! I'll look through the exceptions list and see what looks amiss. You've been immensely helpful!!Lollipop
thank you. this was a solution for my problem with singleTask activity.Maidel
Don't forget to add android:launchMode="singleTop" to your activity on manifest file, check this url for more details about launchMode : intridea.com/blog/2011/6/16/…Cadaverine
Is it mandatory to use super.onResume() and super.onNewIntent() respectively, in those snipets?Mora
Definitely in onResume(), and probably not a bad idea to do it in onNewIntent as well, good call, edited.Robers

© 2022 - 2024 — McMap. All rights reserved.