poniedziałek, 27 maja 2013

How to obtain XAuth access token using Signpost and HttpURLConnection in Android

Goal

Use Signpost library and Android HttpURLConnection to obtain access token which is defined by XAuth authorizations mechanism.
If you are interested with use of Android Apache library then you may be interested with this and skip
the rest of this post.

Preconditions

You should already understand XAuth workflow and Android networking. Knowing Signpost is not a must but a plus.
Server side is not in scope of this post. I assume it is already properly setup and responding.

Solution

Let's create the following method where HttpURLConnection is already initialized. Go through the code and read carefully my comments.
 
public String[] getToken(HttpURLConnection connection) throws IOException,
  OAuthMessageSignerException, OAuthExpectationFailedException,
  OAuthCommunicationException {
 // set request method
 connection.setRequestMethod("POST");
 /**
  * Indicate that this connection allow input and output. We want send
  * data and retrieve some.
  */
 connection.setDoInput(true);
 connection.setDoOutput(true);
 /**
  * Create consumer using class from Signpost. DefaultOAuthConsumer
  * implementation was designed for HttpURLConnection.
  */
 DefaultOAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY,
   CONSUMER_SECRET);
 /**
  * Consumer require POST body to be provided to properly sign request.
  * In case HttpURLConnection we can set data by writing it to connection
  * OutputSteam. When we do it data is automatically streamed to the
  * server. If we call sign() before data it won't be included in signing
  * and if we call sign() after request being streamed it will be to
  * late. To workaround this problem we can use consumer's additional
  * parameters, exactly the same as POST body.
  */
 HttpParameters additionalParameters = new HttpParameters();
 additionalParameters.put("x_auth_username", 
                        URLEncoder.encode(USER_KEY, HTTP.UTF_8));
 additionalParameters.put("x_auth_password", 
                        URLEncoder.encode(USER_PASS, HTTP.UTF_8));
 additionalParameters.put("x_auth_mode", "client_auth");
 consumer.setAdditionalParameters(additionalParameters);
 /**
  * Sign request. There is no magic in there. Singpost takes request
  * parameters, method name and URL. Then use to sign it and create
  * signature. As a result our request gets new header parameter
  * Authorization and the rest of the request is not modified.
  */
 consumer.sign(connection);
 /**
  * Now prepare real POST body and start streaming, request is already
  * properly signed with this values.
  */
 List<BasicNameValuePair> params = Arrays.asList(new BasicNameValuePair(
   "x_auth_username", USER_KEY), new BasicNameValuePair(
   "x_auth_password", USER_PASS), new BasicNameValuePair(
   "x_auth_mode", "client_auth"));
 /**
  * URLEncodedUtils is an utility class from Apache framework but we can
  * use it to keep things simple and avoid reinventing the wheel.
  */
 String postBody = URLEncodedUtils.format(params, HTTP.UTF_8);
 connection.setFixedLengthStreamingMode(postBody.getBytes().length);
 /**
  * IMPORTANT: It is crucial to set Content-Type after singing. Singpost
  * will look for it and when it's found it will try to get request body
  * which would be null. You can have a look at Signpost's
  * HttpURLConnectionRequestAdapter, method getMessagePayload() used for
  * obtaining body always returns null. Apache adapter on the other hand
  * implements this method.
  */
 connection.setRequestProperty("Content-Type",
   "application/x-www-form-urlencoded");
 PrintWriter out = new PrintWriter(connection.getOutputStream());
 out.print(postBody);
 out.close();
 int statusCode = connection.getResponseCode();
 if (statusCode != HttpURLConnection.HTTP_OK) {
  // handle unexpected status codes here
  return null;
 }
 // read response data
 InputStream in = connection.getInputStream();
        InputStreamReader reader = new InputStreamReader(in, HTTP.UTF_8);
 char[] buf = new char[4096];
 StringBuffer buffer = new StringBuffer();
 int i = 0;
 while ((i = reader.read(buf)) != -1) {
  buffer.append(buf, 0, i);
 }
 in.close();
 String responseBody = new String(buffer);
 // our response is an array of key value pairs
 return TextUtils.split(responseBody, "&");
}

Your result can look for example like this:
oauth_token=[generated token value on the server]
oauth_token_secret=[generated token secret on the server]

Later you can use it with Signpost consumer like this:
DefaultOAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY,
  CONSUMER_SECRET);
consumer.setTokenWithSecret(ACCESS_TOKEN, TOKEN_SECRET);
consumer.sign(connection);

That's it!

Brak komentarzy :

Prześlij komentarz