(solved) Java Oauth request_token flow example, without libraries


#1

I wrestled with step 1 on this page (the request_token part)…

https://dev.twitter.com/docs/auth/implementing-sign-twitter

…for essentially 2 full days and just got it working. I was told to use twitter4j and and some of the other ones but that’s problematic for a few reasons; efficiency, security and stability are unknown unless you dig through all the source code and test it yourself. If you’re going to do that, you might as well build it yourself, right?

Anyway, since good examples were hard (er, impossible) to come by, I figured I’d share the wealth of what I learned. First, a couple of clarifying notes that I tripped up on that might help people:

Putting together the request string sucks, but how to do it is well documented here: https://dev.twitter.com/docs/auth/authorizing-request . The request_token flow differs in a few important – and tricky – ways that weren’t entirely obvious at the beginning:

a. Use https://api.twitter.com/oauth/request_token instead of the URL endpoint provided – ok, maybe this one IS obvious

b. request_token has no post body. In other words, ignore the “status=Hello%20Ladies…” part underneath the HTTP headers

c. Do not include any parameters to the request target. That is, the “include_entities” part is unnecessary for request_token. It should just be “POST /oauth/request_token HTTP/1.1” on the top line

d. The signature base string has to be assembled very precisely before Sha-1 hashing it. If you’re not using a callback, leave the “oauth_callback=” part out entirely. The params need to be alphabetized before hashing. The timestamp has to be within 5 minutes of twitter time. And everything has to be escaped properly. Follow the directions (linked above) very very closely.

e. When you’ve finally assembled the signature base string correctly, you have to hash it. Normally this would be done with “consumer_secret&user_access_token” for normal api requests, but we don’t know the user access token yet. So the value to hash against is just “consumer_secret&”. (The & is not escaped in this case as it is just being used to hash against, not for any wire communication.)

f. Use the example data from my first link until you have it right. If you try to execute the requests, they will fail because the key is invalid and because the timestamp is long overdue, but it’s useful to use the example data to match against the oauth_signature it produces: “F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D” Until you’re able to produce that oauth_signature value using the sample data, you’ve still got something wrong in your signature process.

Without further ado, here is my working request_token flow:

  /* The following works with example credentials from: https://dev.twitter.com/docs/auth/implementing-sign-twitter
			    
			    String oauth_consumer_key = "cChZNFj6T5R0TigYB9yd1w"; // fake from example
			    String oauth_nonce = "ea9ec8429b68d6b77cd5600adbbb0456"; // fake from example
			    String oauth_signature_method = "HMAC-SHA1";
			    String oauth_timestamp = "1318467427"; // fake from example
			    String oauth_callback_value = "http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F";
			    // the parameter string must be in alphabetical order
			    String parameter_string = "oauth_callback=" + oauth_callback_value + "&oauth_consumer_key=" + oauth_consumer_key + "&oauth_nonce=" + 
			    	oauth_nonce + "&oauth_signature_method=" + oauth_signature_method + "&oauth_timestamp=" + oauth_timestamp + "&oauth_version=1.0";		
			    System.out.println("parameter_string=" + parameter_string);
			    
			    String signature_base_string = "POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&" + URLEncoder.encode(parameter_string, "UTF-8");
			    System.out.println("signature_base_string=" + signature_base_string);
			    
			    String oauth_signature = "";
			    try {
			    	oauth_signature = computeSignature(signature_base_string, "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg&");  // fake from example, note the & at the end. Normally the user access_token would go here, but we don't know it yet for request_token flow
					System.out.println("oauth_signature=" + URLEncoder.encode(oauth_signature, "UTF-8")); // this should match F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D
				} catch (GeneralSecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			    String authorization_header_string = "OAuth oauth_callback=\"" + oauth_callback_value + "\",oauth_consumer_key=\"" + oauth_consumer_key + "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"" + 
			    		oauth_timestamp + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_version=\"1.0\",oauth_signature=\"" + URLEncoder.encode(oauth_signature, "UTF-8") + "\"";
			    System.out.println("authorization_header_string=" + authorization_header_string);
			    
			    */
			    
			    // This is the working flow
			    
			   	String oauth_signature_method = "HMAC-SHA1";
			    String oauth_consumer_key = "KEYKEYKEYKEYKEY";
			    String uuid_string = UUID.randomUUID().toString();
			    uuid_string = uuid_string.replaceAll("-", "");
			    String oauth_nonce = uuid_string; // any relatively random alphanumeric string will work here. I used UUID minus "-" signs
			    String oauth_timestamp = (new Long(timestamp_at_entry/1000)).toString(); // get current time in milliseconds, then divide by 1000 to get seconds
			    // I'm not using a callback value. Otherwise, you'd need to include it in the parameter string like the example above
			    // the parameter string must be in alphabetical order
			    String parameter_string = "oauth_consumer_key=" + oauth_consumer_key + "&oauth_nonce=" + oauth_nonce + "&oauth_signature_method=" + oauth_signature_method + "&oauth_timestamp=" + oauth_timestamp + "&oauth_version=1.0";		
			    System.out.println("parameter_string=" + parameter_string);
			    String signature_base_string = "POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&" + URLEncoder.encode(parameter_string, "UTF-8");
			    System.out.println("signature_base_string=" + signature_base_string);
			    String oauth_signature = "";
			    try {
			    	oauth_signature = computeSignature(signature_base_string, "3yasdfasmyconsumersecretfasd53&");  // note the & at the end. Normally the user access_token would go here, but we don't know it yet for request_token
					System.out.println("oauth_signature=" + URLEncoder.encode(oauth_signature, "UTF-8"));
				} catch (GeneralSecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			    String authorization_header_string = "OAuth oauth_consumer_key=\"" + oauth_consumer_key + "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"" + 
			    		oauth_timestamp + "\",oauth_nonce=\"" + oauth_nonce + "\",oauth_version=\"1.0\",oauth_signature=\"" + URLEncoder.encode(oauth_signature, "UTF-8") + "\"";
			    System.out.println("authorization_header_string=" + authorization_header_string);
			     
			    String oauth_token = "";
			    HttpClient httpclient = new DefaultHttpClient();
				 try {
					 HttpPost httppost = new HttpPost("https://api.twitter.com/oauth/request_token");
					 httppost.setHeader("Authorization",authorization_header_string);
					 ResponseHandler<String> responseHandler = new BasicResponseHandler();
					 String responseBody = httpclient.execute(httppost, responseHandler);
					 oauth_token = responseBody.substring(responseBody.indexOf("oauth_token=") + 12, responseBody.indexOf("&oauth_token_secret="));
					 System.out.println(responseBody);
			     } 
				 catch(ClientProtocolException cpe)  {	System.out.println(cpe.getMessage());  }
				 catch(IOException ioe) {	System.out.println(ioe.getMessage());  }
				 finally { httpclient.getConnectionManager().shutdown();  }

To compute signatures, use this function I found on stack overflow. It depends on apache-commons-codec:

private static String computeSignature(String baseString, String keyString) throws GeneralSecurityException, UnsupportedEncodingException {

	    SecretKey secretKey = null;

	    byte[] keyBytes = keyString.getBytes();
	    secretKey = new SecretKeySpec(keyBytes, "HmacSHA1");

	    Mac mac = Mac.getInstance("HmacSHA1");

	    mac.init(secretKey);

	    byte[] text = baseString.getBytes();

	    return new String(Base64.encodeBase64(mac.doFinal(text))).trim();
	}

After you’ve got the redirect url, the user will be directed to twitter to grant access. In my example, they are shown a pin which then has to be plugged in elsewhere. But at least I’ve got Step 1 nailed down!


How to generate oAuth Header String in Android for requesting Twitter API 1.1?
#2

Hi, this looks good. Once you get your request token are you able to poste a tweet to twitter using this code?

Can you put your code on github?


#3

Here you go. Four Java examples:

  1. Get request token
  2. Use request token & user-supplied auth code to get user access token
  3. Use access token to search tweets
  4. Use access token to update status

#4

Thank you for this! Was very helpful


#5

Thank you so much…It was very helpful :slight_smile:


#6

Thanks a lot it was very useful …


#7

Cyrus, thanks so much for your github repository example, this is a great resource.

I took your code, took it apart to understand how it worked, refactored it somewhat, and then incorporated it in a JSF/Java application, configured to run with Maven on OpenShift with JBoss AS7.

https://github.com/ams10961/siwtjsf.git

Thanks for sharing it…

Andrew


#8

hi,
Thanks for the code.It is very helpful.
But in the above code there is no proxy involved in between the call to twitter.
In my case there is proxy involved so i would like to know how the above code can be modified so as to make a call to api.twitter.com via proxies.

Any documentation or help is really appreciable.I have googled it in every possible way but could not get a working code.

Thanks in Advance

Deepak


#9

thanks a lot. very very helpful.


#10

Tremendous help, thank you.


#11

hello, thank you for the example.
I have a question, how can I integrate the authentication code in my servlet to execute the code built into the header and have a callback to my web site?