I have spent the last few days getting an OAuth implementation up and running. Not on Android, but on my web server that will act as the proxy to the OAuth protected service. I'm just about to implement my Android client and my head is still churning over security and implementation issues.
OAuth is messy enough when the client is just a web browser. You have the following series of steps:
- (client web browser) make request to my proxy server
- (proxy server) request unauthorized token from OAuth provider (e.g. - web service API)
- (proxy server) ask OAuth provider to have user authorize the token. Redirect web browser to OAuth provider's 'authorize' URI
- (OAuth provider) after user completes authorization, redirects browser to your callback URI
- (proxy server::callback URI) Exchange authorization token for an access token and then store it for future calls
- Make API calls to OAuth provider and return response document to client web browser
Now that's quite enough as it is. But when using the same mechanics with a mobile app as a client it gets even more involved. The problem is of course that you have to do some acrobatics to inject a browser session into your mobile app's code flow while doing the OAuth dance. This means you have to further complicate the OAuth dance as follows (I am using an Android app for this example to make things concrete):
- (mobile web app::native code) make request from proxy server using Java/HTTP
- (proxy server) request unauthorized token from OAuth provider (e.g. - web service API)
- (proxy server) return a response document to the mobile web app that contains the redirect URI for user authorization by the OAuth provider, preferably obfuscated to hide the consumer key and other details to deter "over-the-air" snooping.
- (mobile web app) Launch a web browser activity with the authorization URL with an intent filter installed. However, store and then replace the proxy server's specified callback URI with a special "phony" URI crafted for easy URI identification by an intent filter (see following steps)
- (OAuth provider) after user completes authorization, redirect browser to your "phony" callback URI.
- (mobile web app) Intent filter detects "phony" callback URI and uses that signal to regain control. Rebuild the proxy server's callback URI and use Java/HTTP to execute the request.
- (proxy server::callback URI) Exchange authorization token for an access token and then store it for future calls as before
- Make API calls to OAuth provider and return response document to mobile web app
This is rather hideous as you can see. If there is a much simpler way of doing this then I am eager to hear it. As far as I know there only two other alternatives, each with their own significant problems.
1) Forget about a proxy server and do everything directly from the mobile web app. The big security hole here is that you have to "bake" in your OAuth consumer key and secret into your app. If an attacker decompiles your code and hunts down those strings, a fairly easy operation for someone who is experienced at reverse engineering, they can wreak havoc with your application and users.
2) Switch to XAuth where the user provides you with their login name and password, and you "agree" not to store them and exchange them directly for an access token with the XAuth server. The first problem is gaining user trust to provide that information, the problem OAuth was created to solve, and of course what about people that don't honor their commitment to discard the login details? Worse, the XAuth server must support XAuth and offer HTTPS (SSL) connections and I have seen tons of web API's that don't support either. An SSL connection is necessary of course because without it you'd send the user's login name and password over the wire in plain text when making your XAuth request.
http://blog.zyber17.com/post/1283741364/why-xauth-is-a-really-dumb-idea
FYI, although it does not use a proxy server the following example illustrates the technique I described above for injecting a browser session into your mobile app OAuth interaction and intercepting the callback URI:
http://blog.doityourselfandroid.com/2010/11/10/oauth-flow-in-android-app/
So it seems pretty ugly any way you look at it and I didn't even get into the additional annoyance of creating and managing your own user ID for the user so you can look up their access tokens stored on your proxy web server properly (including the messiness of one more PIN or access code for the user to manage).
Interesting side note: When doing some research on an Android web browser session security I was pleased to find out that you are not allowed access to the current web page HTML. If you could access it, then a hostile Android coder could easily sniff out the user's login and password thereby defeating the purpose and intent of OAuth entirely. I was dismayed to find out that there may be a way to get that HTML by way of the CacheManager object. Curiously enough that object is now deprecated and schedule for removal according to the Android docs so hopefully that means Google discovered the (potential) security hole and is taking steps to remove it in an upcoming build:
http://developer.android.com/reference/android/webkit/CacheManager.html
In closing, I'd like to hear the thoughts of those out there that have struggled with these very same issues when creating their OAuth applications.
-- roschler