3

I have an Android client that needs to authenticate with a python Google App Engine app using OAuth. I followed this article.

And was able to successfully do so using an HTTP Get Request. The Android client uses the package com.google.api.client to accomplish this:

OAuthHmacSigner _signer;
HttpTransport TRANSPORT = new NetHttpTransport();
_signer = new OAuthHmacSigner();
_signer.clientSharedSecret = CONSUMER_SECRET;

// STEP1: get a request token
OAuthGetTemporaryToken requestToken = new OAuthGetTemporaryToken(REQUEST_TOKEN_URL);
requestToken.consumerKey = CONSUMER_KEY;
requestToken.transport = TRANSPORT;
requestToken.signer = _signer;
requestToken.callback = "http://my_app_specific_callback";
requestTokenResponse = requestToken.execute();
OAuthAuthorizeTemporaryTokenUrl authorizeUrl = new OAuthAuthorizeTemporaryTokenUrl(AUTHORIZE_URL);
authorizeUrl.temporaryToken = requestTokenResponse.token;
// at this point, redirect the user using a WebView to the URL string returned by authorizeUrl.build().  Continue below once the user has granted request.

// STEP2: get an access token
_signer.tokenSharedSecret = requestTokenResponse.tokenSecret;
OAuthGetAccessToken accessToken = new OAuthGetAccessToken(ACCESS_TOKEN_URL);
accessToken.consumerKey = CONSUMER_KEY;
accessToken.signer = _signer;
accessToken.transport = TRANSPORT;
accessToken.temporaryToken = requestTokenResponse.token;
accessTokenResponse = accessToken.execute();

// STEP3: use the access token acquired above to access a protected resource
_signer.tokenSharedSecret = accessTokenResponse.tokenSecret;
OAuthParameters parameters = new OAuthParameters();
parameters.consumerKey = CONSUMER_KEY;
parameters.token = accessTokenResponse.token;
parameters.signer = _signer;
HttpRequestFactory factory = TRANSPORT.createRequestFactory(parameters);
HttpRequest req = factory.buildGetRequest(new GenericUrl(PROTECTED_URL_GET_USER_EMAIL));
com.google.api.client.http.HttpResponse resp = req.execute();

In the above code snippet, all the 3 steps work just fine. And on my Google App Engine server, I inspected the GET request made to the PROTECTED_URL_GET_USER_EMAIL, and it contained a proper authentication HTTP Header:

'Authorization': 'OAuth oauth_consumer_key="shiprack-test1.appspot.com", oauth_nonce="...", oauth_signature="...", oauth_signature_method="HMAC-SHA1", oauth_timestamp="...", oauth_token="..."'

Using the oauth python package on GAE (google.appengine.api.oauth), my server is able to authenticate the user and determine the user's email address (oauth.get_current_user()).

However, the problem is when I convert the PROTECTED_URL_GET_USER_EMAIL to an HTTP Post request. This is how I do that:

Map<String, String> paramsMap = new HashMap<String, String>();
paramsMap.put("param1", "value1")
paramsMap.put("param2", "value2")
HttpRequest req = _httpRequestFactory.buildPostRequest(new GenericUrl(url), new UrlEncodedContent(paramsMap));
req.setFollowRedirects(true);
com.google.api.client.http.HttpResponse resp = req.execute();

But now, on the GAE python server side, I'm not able to determine the current user. The HTTP Headers contain the same OAuth Authentication headers (with a different nonce, timestamp and signature, but the same oauth token). The "param1" and "param2" are in the HTTP Payload. Perhaps my POST request is not constructed properly?

I used Ikai Lan's (Google App Engine support team) code for a python client to authenticate against my GAE server. And this worked too... the original client with the GET request, and even when I modified it to use a POST request. I noticed though that with the POST request, the oauth parameters were included as URL encoded values in the HTTP payload instead of the in the HTTP header. Is this a requirement for Oauth-signed HTTP post requests?

Thanks in advance!

n00begon
  • 3,503
  • 3
  • 29
  • 42
Shiprack
  • 1,095
  • 11
  • 18
  • I found out that not all HTTP Post requests signed by an oauth signature fail. Only those that have url encoded form data with app specific key/value pairs. Empty HTTP Post requests (with the Oauth parameters embedded in the HTTP headers) work just fine, as do HTTP Get requests. – Shiprack Jan 16 '12 at 13:02
  • More updates: Any HTTP PUT request that has URL Encoded data does not work. If I encode application/json, for example, the GAE server is able to determine the authenticated user from the oauth Authentication HTTP headers that my Android client app sends (using the google client java api). Also, I expected the OAuthHmacSigner class to automatically add the oauth parameters as URL Encoded payload data if I provided app-specific payload data that was also URL Encoded. Oauth spec indicates that these parameters must be alphabetically listed. But this did not happen. – Shiprack Jan 17 '12 at 03:40
  • For anyone trying to do this - encoding app-specific data as application/json, and signing it with oauth credentials, here's a good post on how to ensure your json formatting is legit: http://www.bennadel.com/blog/557-JSON-Unterminated-String-Literal-Error.htm – Shiprack Jan 17 '12 at 14:05

1 Answers1

1

Unfortunately, proper OAuth 1.0a encoding based on form-encoded HTTP content parameters is not implemented yet. We've gotten quite a few requests for this. There is a feature request already open for it.

n00begon
  • 3,503
  • 3
  • 29
  • 42
Yaniv Inbar
  • 1,409
  • 9
  • 10
  • Ah that explains everything! Thanks Yaniv. I suppose I should just resort to using application/json Content-type. Do you foresee any problems with that (from an oauth signing point of view). I'll have to focus my efforts on ensuring my app-specific data can be properly json formatted (and decoded at the server). – Shiprack Jan 17 '12 at 13:55