Rutvij's Odyssey

Life of a Developer and Technologies he plays with

Archive for June 2013

Playframework issue with OAuth request with query parameters

leave a comment »

I am playing with playframework and building sample application with LinkedIn. I am using awesome secure social play plugin to authenticate users to LinkedIn.

SecureSocial is great framework providing support for OAuth 1 & OAuth 2 and integrate easily in play.

My use case was simple, once user logged in to app ( LinkedIn Oauth 1.0), application makes call to retrieve user’s connections using Linkedin API.


@SecureSocial.SecuredAction
public static Result index() {
Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
Option<OAuth1Info> info = user.oAuth1Info();
OAuth1Info oAuth1Info = info.get();
String consumerKey = Play.application().configuration()
.getString("securesocial.linkedin.consumerKey");
String consumerSecret = Play.application().configuration()
.getString("securesocial.linkedin.consumerSecret");
// Getting URL from conf
String url = Play.application().configuration()
.getString("myapp.profile.url");
ConsumerKey key = new ConsumerKey(consumerKey, consumerSecret);
RequestToken token = new RequestToken(oAuth1Info.token(),
oAuth1Info.secret());
// Using BuiltIn OAuthCalculator to generate signature for request
OAuthCalculator calc = new OAuthCalculator(key, token);
String response = WS.url(url).setHeader("x-li-format", "json")
.sign(calc).get().get().getBody();
return ok(response);
}

I had configured “myapp.profile.url to use following url  http://api.linkedin.com/v1/people/~/connections

It worked fine and returned all the connections,but when I modified url to use query parameters to do people search : http://api.linkedin.com/v1/people-search?last-name=shah
I got following error :

{
"errorCode": 0,
"message": "[unauthorized]. OAU:***************************************",
"requestId": "LST33GHM3E",
"status": 401,
"timestamp": 1371245720144
}

To debug this I put TCPMon proxy between Play and linkedin and found WS.url is ignoring query params passed in url.

I changed code  to add query parameter this way : .setQueryParameter("last-name", "shah") 

But this has not changed the response and returned unauthorized error.

It took me while to realize that Playframework’s OAuthCalculator is not generating signature correctly and little googling confirmed it https://github.com/playframework/Play20/issues/1159

Now I have chicken & egg problem, WS.url does not take querystring and using .setQueryParameter not getting included in generated signature.

I started digging into Play code and found that Play’s WS.url APIs are wrapper around Ning Aync http client.

I refactored code to directly using ning’s http client along with its OAuth calculator and it worked fine :


@SecureSocial.SecuredAction
public static Result index() {
Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
Option<OAuth1Info> info = user.oAuth1Info();
OAuth1Info oAuth1Info = info.get();
String consumerKey = Play.application().configuration()
.getString("securesocial.linkedin.consumerKey");
String consumerSecret = Play.application().configuration()
.getString("securesocial.linkedin.consumerSecret");
String url = "http://api.linkedin.com/v1/people-search&quot;;
/*** Modified OAuth Request code to use Ning's AyncHttpClient ***/
OAuthSignatureCalculator calc = null;
com.ning.http.client.oauth.ConsumerKey consumerAuth1 = new com.ning.http.client.oauth.ConsumerKey(
consumerKey, consumerSecret);
com.ning.http.client.oauth.RequestToken userAuth1 = new com.ning.http.client.oauth.RequestToken(
oAuth1Info.token(), oAuth1Info.secret());
calc = new OAuthSignatureCalculator(consumerAuth1, userAuth1);
AsyncHttpClient client = new AsyncHttpClient();
client.setSignatureCalculator(calc);
try {
Future<Response> f = client.prepareGet(url)
.addQueryParameter("last-name", "shah")
.addHeader("x-li-format", "json").execute();
String responseBody = f.get().getResponseBody();
return ok(index.render(responseBody));
} catch (Exception e) {
logger.error("Exception while getting people" + e, e);
}
return internalServerError("Error");
}

TLDR 😉

Playframeork 2.1.0 OAuthCalculator has bug so it does not work with url contains queryString, use Ning’s AyncHttpClient instead.

Written by rutvijshah

June 14, 2013 at 3:05 pm