In the spirit of community giving here’s step by step tutorial on how to do OAuth Twitter status updates on Android
Prerequisites
I’m using most excellent oauth-signpost library for this tutorial. You will need both core and commonshttp4 jars which you should simply drop into your project/assets folder and add to your Eclipse’s build path
Register App
Go and register your new wonderful app on Twitter. When filling the form don’t forget to choose “browser” as “Application Type”. If you fail to do that or if your current app doesn’t have that option selected then you won’t be able to redirect back to your Android app after Twitter authorization step. The form asks for “Callback URL”, you can enter any valid URL doesn’t matter which one since you will provide the “real” one programmatically.
Note “Consumer key” and “Consumer secret” values – you will need these in your app
Common code
Create consumer and provider where you see fit so you can reuse these later
CommonsHttpOAuthConsumer consumer = new CommonsHttpOAuthConsumer(
CONSUMER_KEY, CONSUMER_SECRET, SignatureMethod.HMAC_SHA1);
OAuthProvider provider = new DefaultOAuthProvider(consumer,
"http://twitter.com/oauth/request_token", "http://twitter.com/oauth/access_token",
"http://twitter.com/oauth/authorize");
HttpClient client = new DefaultHttpClient();
Authorization
Create some sort of trigger in your Android app such as checkbox “Enable Twitter”. When user triggers it the following should happen in your code
// Context ctx whichever way you passing it in String authUrl = provider.retrieveRequestToken(CALLBACK_URL); ctx.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
Now – the CALLBACK_URL would be something like yourapp://twitt and basically it will be loaded by external browser if authentication/authorization is successful
So what will happen when this code is executed? The external browser will come up and display Twitter’s “Authorize Application Access” page. When user logs in and clicks “Allow” you should see message telling you that permission is granted and you will be redirected back.
Back from authorization
For your app to pick that app here are next steps
Add intent filter
Open AndroidManifest.xml and add the following lines to the activity that you want to handle further processing
Note “data” element – scheme and host should match your callback URL (remember yourapp://twitt?)
In Android when browser calls that callback URL then your app is brought forward and activity’s onResume() method is called. So in that method you should place the following code that basically gets you token and token_secret
// this must be places in activity#onResume()
Uri uri = this.getIntent().getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
String verifier = uri.getQueryParameter(OAUTH_VERIFIER);
// this will populate token and token_secret in consumer
provider.retrieveAccessToken(verifier);
}
At this point you can call consumet#getToken() and consumer#getTokenSecret() to get and save token/secret for subsequent calls. According to Twitter – access token will not expire unless revoked by user
Posting update
Well, congrats – if you were able to execute all the steps above you now ready to post your first update. Here’s the code that will do it
// create a request that requires authentication
HttpPost post = new HttpPost("http://twitter.com/statuses/update.xml");
final List nvps = new ArrayList();
// 'status' here is the update value you collect from UI
nvps.add(new BasicNameValuePair("status", status));
post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
// set this to avoid 417 error (Expectation Failed)
post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
// sign the request
consumer.sign(post);
// send the request
final HttpResponse response = client.execute(post);
// response status should be 200 OK
int statusCode = response.getStatusLine().getStatusCode();
final String reason = response.getStatusLine().getReasonPhrase();
// release connection
response.getEntity().consumeContent();
if (statusCode != 200) {
Log.e("TwitterConnector", reason);
throw new OAuthNotAuthorizedException();
}
Now – you can use different update URL to get JSON for example, but generally you can use code above to sign any of your Twitter requests.
Happy Twitting!!
P.S. If you decide to re-post this article please kindly provide link back to the original. And if you have Android phone checkout DroidIn - LinkedIn app.
For the posting new things to twitter where is the input. Where would i put the code so i can put the string for app user.
Maybe I don't understand the question. If you are talking about collecting the input that you want to end up on Twitter then you can have a textbox with a button. Get the value from the box, call it "status" and when user clicks the button – execute very last code snippet I put on this page. This particular line adds the value as POST parameter to the request
nvps.add(new BasicNameValuePair("status", status));
Does your application run in a Web Browser or a Desktop Client?
Browser uses a Callback URL to return to your App after successfully authentication.
Client prompts your user to return to your application after approving access.
How do i use a callback url if you don't have a url to callback to.
My application is running on Android phone. Any android activity can be called with URL by using intent filter, the article gives clear example of that. I would read a good Android book before doing any sort of development – this is pretty complicated framework and you have to learn some basics there
My application is running on Android phone. Any android activity can be called with URL by using intent filter, the article gives clear example of that. I would read a good Android book before doing any sort of development – this is pretty complicated framework and you have to learn some basics there
yeah i trying to read a book and develop at the same time i had no idea it would be this difficult, but thanks for the advice.
Don't get easily discouraged. What helped me was to look at "real" opensource Android project such as andtweet (http://is.gd/1Rdq7) It is using older REST API but codewise it's just about perfect, you can learn a lot just by looking. Try to pick the basics – activities, intents, services, etc. and look at their lifecycle through the app, everything will become much clearer
Thanks for the article. I'm following along and when I get to the following line:
provider.retrieveAccessToken(verifier);
I receive this exception:
"oauth.signpost.exception.OAuthExpectationFailedException: Authorized request token or token secret not set. Did you retrieve an authorized request token before?"
Do you know what I can do to solve this problem? Thank you.
Yes Noah. I actually discovered it myself. The problem is – when the browser is open, the activity that holds handle to consumer and provider dies, so when you back to it from callback URL, the activity is anew and so are consumer and provider.
The way (may not be ideal) that I dealt with it – I had to load initial auth request not into standalone browser but into Activity in the same app. Since I save consumer and producer into static class even if 1st activity is stopped the app thread survived and so do consumer and producer. Then the very same callback calls 1st activity (preferences in my case)
I created a browser activity and used that to authenticate with Twitter and send the information back. Seems to be working fine now. Thanks again for the great article and quick response!
Np, always glad to help a fellow programmer :)
droidin, thanks for the posting, it's very helpful. i have the same problem and couldn't resolve because i don't understand what you said above about "load initial auth request not into standalone browser but into Activity in the same app" and "save consumer and producer into static class". do you mind elaborate?
1. You have a choice to call Twitter authentication page in mobile (standalone) browser or inside your app into separate Activity that contains WebView widget
2. I'm putting these into the static class since I need to guarantee that when I'm making subsequent calls I'm referring to the same consumer/producer and not to the newly created non-initialized objects.
Hope this helps
I dont use the Twitter Oauth but Tripit Oauth (very similar) and I also have this issues. I open the authentication page in my standalone browser and then make a callback. But this is where it goes wrong. it makes a new consumer/producer object. How can I make a static class of these 2 objects? Can you give me some advice?
Hey,
How can i do this:
"Since I save consumer and producer into static class even if 1st activity is stopped the app thread survived and so do consumer and producer."
I am trying to authenticate with tripit (with oauth) and I can get my oauth_token but when after the callback new activity is called and I have an new consumer en provider. How can i do this? Thank you,
Wouter
Is there a way to persist the provider/consumer through SharedPreferences or some other means? That way when returning from the callback, you could recreate a valid provider/consumer?
i handled this by setting the activity's launch mode in the android manifest so that there is only ever one instance
android:launchMode="singleInstance"
see http://developer.android.com/guide/topics/fundame...
note: if you do this, then you need to handle the return to your activity in onNewIntent(Intent) rather than onResume() …if my understanding is correct… this.getIntent() gives you the intent that started the activity, not the most recent intent that's calling your activity back to the front. The intent passed to onNewIntent(intent) has the info you need.
i guess it depends on your situation as to what makes more sense… just thought i'd toss this out there since i was running into the same problem
thanks so much for this sample using oauth/signpost on android – super useful :)
Thanks for the tips Ellie, these are very valid points matter of fact – I'm going to try singleInstance right now!
Just to let you people know – setting launchMode to "singleInstance" created some funky behavior and I had to turn it off
Hi Droidin, thanks for the detailed info.
Can you also please provide a list of HTTP requests for using OAuth with Twitter
For example the first request may be => HTTP GET request with consumer_key and consumer_secret as query string parameters (or HTTP headers ?). Another HTTP message would be from Twitter with the oauth_token etc.
Seeing a list of these HTTP requests (in sequence) will be very helpful for people like me who know how to send a request, add intent filter etc. but don't know twitter oauth (and aren't writng android code)
Hi Ram,
Oauth-signpost suppose to abstract that level of details. If you are interested on inner-workings of OAuth (and OAuth on Twitter) there's pretty detailed documentation on OAuth site
Hi Ram,
Oauth-signpost suppose to abstract that level of details. If you are interested in inner-workings of OAuth (and OAuth on Twitter) there's pretty detailed documentation on OAuth site
OK thanks, Droidin.
I'm not writing Java apps, so I can't use Oauth-signpost.
I had taken a look at the oAuth site, perhaps, I need to review it more closely. For instance, the site seems to indicate that the service provider will return an oauth_token parameter and an oauth_token_secret parameter. However, after auth, my callback url gets an oauth_token parameter from twitter, but not an oauth_secret parameter. Anyway, I'll experiment a bit more.
Thanks for the response.
Ram,
The first call to the request URL sends you only a oauth_token , with that you call the access URL to exchange that request token for an access token (the one used for authentication), only there a secret is returned.
For the first calls you need to sign the params in the Authentication header with an empty string for Token Secret, don't remember the exact order but its somthing like this : UrlEncode(Consumer_Secret)+'&'+UrlEncode(Token_Secret) , that should give you the key to encode the Signature base string using HMAC_SHA1.
Hope i was helpfull,
Gaston.
PS: Im not a native english speaker, sorry if its not clear enough
I cannot for the life of me get this to work. Every single time I get a "Received Authentication Challenge is null" error. I have no clue what I am doing wrong. Any thoughts?
Looks like your are passing null for a token. You should use debugger ans step through it and see what are you passing to the call
Hi there!
I'm having some trouble with my code :-(. It seems that provider.retrieveRequestToken(CALLBACK_URL) does not work, as the browser is always lauched with "http://www.google.de". Redirecting to "myapp://twitter" is working fine. I just don't know what's going wrong…
public class HelloAndroid extends Activity {
private final String CONSUMER_KEY = "…";
private final String CONSUMER_SECRET = "…";
private final String CALLBACK_URL = "myapp://twitter";
CommonsHttpOAuthConsumer consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET, SignatureMethod.HMAC_SHA1);
OAuthProvider provider = new DefaultOAuthProvider(consumer, "http://twitter.com/oauth/request_token", "http://twitter.com/oauth/access_token", "http://twitter.com/oauth/authorize");
HttpClient client = new DefaultHttpClient();
/** Called when the activity is first created. */
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
final Button button = new Button(this);
button.setText("Twitter!");
setContentView(button);
final TextView tv = new TextView(this);
button.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String authUrl = "http://www.google.de";
try
{
authUrl = provider.retrieveRequestToken(CALLBACK_URL);
tv.setText(authUrl);
setContentView(tv);
}
catch (Exception e) {}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));/**/
}
});
}
Thanks a lot for your help! :-)
I would look at the net output with some type of network sniffer. google.de may have some javascript or redirection that prevents proper processing
This line:
String verifier = uri.getQueryParameter(OAUTH_VERIFIER);
Should actually be this:
String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
or this:
String verifier = uri.getQueryParameter("oauth_verifier");
Well – in my case it's just a static String that holds value "oauth_verifier"
Thanks for the detailed info. in my application, when browser re-directs, the callback_URL is always taken as the one specified in my twitter application settings. the callback_URL specified programmatically is not considered at all. any idea why this happens?
Hi Droidin,
Thanks for this wonderful tutorial. i have a problem with my App in Android. Redirect is always happening to the URL specified in my application settings in Twitter. the URL provided programatically is never considered. Can u tell me some of the causes for this?
You may want to get signpost code and debug through your case. Also look at NET traffic with a sniffer such as Firebug
Great post, thanks for sharing!
One question: it all works fine for me (did the singleInstance fix to handle the exception mentioned in post from Noah), after a successful twitter login via the browser I get sent back to the app. But, the browser is never closed (the one that says "Redirecting you back to the application"). I see that it's still there when I close my app.
Is that the expected behaviour, or does that have to do with the singleInstance fix? Or has this to do with the fact that you're redirecting back to an app, not a HTTP URL in the browser?
Regards,
Marc
oauth/authorize
I think this is normal behavior. It is doing the callback to your app, and the browser is just sitting there waiting to be used or shutdown by the OS to free up resources (ad determined by the OS).
Great tutorial. Very helpful!
Receiving 401 Unauthorized error following client.execute(post);
Consumer looks like this (XXXX's appear to be valid, double checked consumer key and secret):
"consumer"= CommonsHttpOAuthConsumer (id=830055661728)
consumerKey= "XXXXXXXXXXX" (id=830055471400)
consumerSecret= "XXXXXXXXXXX" (id=830055471496)
messageSigner= HmacSha1MessageSigner (id=830055661760)
base64= Base64 (id=830055661784)
consumerSecret= "XXXXXXXXXXX" (id=830055471496)
tokenSecret= "XXXXXXXXXXX" (id=830055700408)
signatureMethod= SignatureMethod (id=830055477184)
token= "XXXXXXXXXXX" (id=830055699896)
What am I doing wrong?
try this, theres state loss when going to the browser to authenticate with twitter, its not the most efficient but I think its the cleanest..
http://stackoverflow.com/questions/1965568/oauth-...
This is useful code, thanks I will be trying it later.
Here a working example on how to use SignPost and Twitter4J with OAuth: http://code.google.com/p/agirardello/
If you are constantly getting errors from provider.retrieveRequestToken, MAKE SURE YOUR CLOCK IS SET RIGHT.
I am getting an error with :
provider = new DefaultOAuthProvider(consumer,
"http://twitter.com/oauth/request_token",
"http://twitter.com/oauth/access_token",
"http://twitter.com/oauth/authorize");
saying that the constructor doesn't take consumer string string string, just string string string. What am I doing wrong?
Also, I am getting SignatureMethod cannot be resolved. What do I do to make that work?
Thank you !!!!
Well the trick here is to come back to the same consumer/producer. In my app I make a singleton which lives in the main thread so until I stay with my app I can (to the certain degree) assume that m y static singleton will survive. That is one reason why I recommend to open Twitter login page in the WebView embedded into your Activity rather than in standalone browser
You can create a new consumer and provider.
You have to save the token and secret (consumer getter) from the first request only to set the token with secret for the new instance of consumer.
The token and secret have to be set to get the access token and -secret with the secrets of prior valid tokenrequest.
Easy, no singleton, no webview … :-)
mg, do you have any thoughts on where to save it, aside from persistent storage, I would love not to have to move away from singleton and webview approach?
these are my current steps:
activity initial creation
provider creation
go to auth url
login w/ twitter
sent back to app
oncreate is getting called and recreating my provider
(this is where the token is loss, so when I call retrieveAccessToken, the above error occurs)
mg, I tried saving the token and token_secret and then recreating the consumer, but now I get this issue.
Authorization failed (server replied with a 401). This can happen if the consumer key was not correct or the signatures did not match.
( My consumer keys is correct but, the signatures are different though when you recreate the consumer with the secret and token)
Any thoughts or examples you can provide?
I use SharedPreferences to save any token and secret.
1. Retrieve request token …
… and save CommonsHttpOAuthConsumer.getToken() and CommonsHttpOAuthConsumer.getTokenSecret()
2. Retrieve access token …
… create new CommonsHttpOAuthConsumer and set the token and secret from first request (CommonsHttpOAuthConsumer.setTokenWithSecret(saved_token, saved_token_secret)).
since I'm replying to myself, this is what I ended up doing ……….
1) request for auth url and tokens
2) serialize and persist the provider to a file (dont really like it but it works)
3) send user to twitter auth page
4) user redirected back to app
5) deserialize the provider into current activity
6) request access tokens from twitter
Taken from here, http://stackoverflow.com/questions/1965568/oauth-...
Thank you.
I use the SharedPreferences to save token and secret.
Then the problem : "Authorized request token or token secret not set. Did you retrieve an authorized request token before?" is fixed.
But I get another error -> "oauth.signpost.exception.OAuthNotAuthorizedException: Authorization failed (server replied with a 401). This can happen if the consumer key was not correct or the signatures did not match."
when I use callback method ,then I get an oauth_verifier=szAlKvSv0MpcqVzbHjYMnLJRPY0c8LsLhJb6LXM
<- Is it right !?
If it is right , can I use provider.retrieveAccessToken(oauth_verifier) to get the Access token and Token secret ?
Because I use this oauth_verifier , then I always get the "Authorization failed (server replied with a 401)".
Thanks all…
I use webview to open Twitter authentication page, then those problems was fixed.
Thanks again … ^^
Sorry ,Can you put the sample code for how to use thw web view?