Document directory
- Why oauth2.0 matters
- Preparation
- Choosing an account
- Getting a token
- Registering your app
- Using your token
Google Play services and oauth identity tools
Http://android-developers.blogspot.com/2012_09_01_archive.html
Posted bytim bray
The rolout of Google Play services to all Android 2.2 + devices worldwide is now complete, and all of those devices now have new tools for working with oauth 2.0 tokens. this is an example of the kind of agility in rolling out new platform capabilities that
Google Play Services provides.
Why oauth2.0 matters
The Internet already has too implements usernames and passwords, and they don't scale. furthermore, your Android device has a strong notion of who you are. in this situation, the industry consensus is that oauth 2.0 is a good choice for the job, offering the promise
Of strong security minus passwords.
Google Play services make oauth 2.0 authorization available to Android apps that want to access Google APIs, with a good user experience and security.
Typically, when you want your Android app to use a Google account to access something, you have to pick which account on the device to use, then you have to generate an oauth 2.0 token, then you have to use it in your HTTP-based dialogue with the Resource
Provider.
These tasks are largely automatically for you if you're using a recent release of thegoogle APIs client library for Java; the discussion here applies if you want to access the machinery directly,
For example in sending your own HTTP gets and posts to a restful interface.
Preparation
Google Play services has just started rolling out, and even after the rolout is complete, will only be available on compatible Android devices running 2.2 or later. this is the vast majority, but there will be devices out there where it's not available.
It is also possible for a user to choose to disable the software.
For these reasons, before you can start making cals, You have to verify that Google Play services is installed. To do this, callisgoogleplayservicesavailable ().
The result codes, and how to deal with them, are encoded ented in theconnectionresult class.
Choosing an account
This is not, and has never been, rocket science; there are using examples online that retrieve accounts from Android's accountmanager and display some sort of pick list. the problem is that they all have their own look and feel, and for something like this,
Which touches on security, that's a problem; the user has the right to keep CT consistency from the system.
Now you can use the handy
Accountpicker. newchooseaccountintent () method to give you an intent; feed it to startactivityforresult () and you'll launch a nice standardized user experience that will return you an account (if the user feels like providing one ).
Two things to note: When you're talking to these APIs, they require a Google account (accountmanager can handle multiple flavors), so specifygoogleauthutil. google_account_type
Argument as the value forallowableAccountTypes
Argument. Second, you don't need an android. Accounts. Account object, you just use the email-address string (available in account. Name) that uniquely identifies it.
Getting a token
There's really only one method call you need to use,
Googleauthutil. gettoken (). it takes three arguments: a context, an email address, and another string argument called scope. every information resource that is willing to talk oauth 2.0 needs to publish which scope (or scopes) it uses. for example, to access
The Google + API, the scope isoauth2:https://www.googleapis.com/auth/plus.me
. You can provide multiple space-separated scopes in one call and get a token that provides access to all of them. Code like this might be typical:
private final static String G_PLUS_SCOPE = "oauth2:https://www.googleapis.com/auth/plus.me"; private final static String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.profile"; private final static String SCOPES = G_PLUS_SCOPE + " " + USERINFO_SCOPE;
In an ideal world, gettoken () wocould be synchronous, but three things keep it from being that simple:
The first time an app asks for a token to access some resource, the system will need to interact with the user to make sure they're OK with that.
Any time you ask for a token, the system may well have a network conversation with the identity back-end services.
The infrastructure that handles these requests may be heavily loaded and not able to get you your token right away. rather than keeping you waiting, or just failing, it may ask you to go away and come back a little later.
The first consequence is obvious; you absolutely can't call gettoken () on the UI thread, since it's subject to unpredictable delays.
When you call it, the following things can happen:
It returns a token. That means that everything went fine, the back-end thinks the authorization was successful, and you should be able to proceed and use the token.
It throws
Userrecoverableauthexception, which means that you need to interact with the user, most likely to ask for their approval on using their account for this purpose. the exception has a getintent () method, whose return value you can feed to startactivityforresult ()
To take care of that. Of course, you'll need to be watching for the OK in the onactivityresult () method.
It throws an ioexception, which means that the authorization infrastructure is stressed, or there was a (not terribly uncommon on mobile devices) Networking error. you shouldn't give up instantly, because a repeat call might work. on the other hand, if you
Go back instantly and pester the server again, results are unlikely to be good. So you need to wait a bit; best practice wocould be the classic Exponential-Backoff pattern.
It throws
Googleauthexception, which means that authorization just isn't going to happen, and you need to let your user down politely. this can happen if an invalid scope was requested, or the account for the email address doesn't actually exist on the device.
Here's some sample code:
try { // if this returns, the OAuth framework thinks the token should be usable String token = GoogleAuthUtil.getToken(this, mRequest.email(), mRequest.scope()); response = doGet(token, this); } catch (UserRecoverableAuthException userAuthEx) { // This means that the app hasn't been authorized by the user for access // to the scope, so we're going to have to fire off the (provided) Intent // to arrange for that. But we only want to do this once. Multiple // attempts probably mean the user said no. if (!mSecondTry) { startActivityForResult(userAuthEx.getIntent(), REQUEST_CODE); response = null; } else { response = new Response(-1, null, "Multiple approval attempts"); } } catch (IOException ioEx) { // Something is stressed out; the auth servers are by definition // high-traffic and you can't count on 100% success. But it would be // bad to retry instantly, so back off if (backoff.shouldRetry()) { backoff.backoff(); response = authenticateAndGo(backoff); } else { response = new Response(-1, null, "No response from authorization server."); } } catch (GoogleAuthException fatalAuthEx) { Log.d(TAG, "Fatal Authorization Exception"); response = new Response(-1, null, "Fatal authorization exception: " + fatalAuthEx.getLocalizedMessage()); }
This is from a sample library I 've posted on code.google.com with an authorizedactivity class that implements this. We think that some of this authorization behavior is going to be app-specific,
So it's not clear that this exact authorizedactivity recipe is going to work for everyone; but it's Apache2-licensed, so feel free to use any pieces that work for you. it's set up as a library project, and there's also a small sample app called G + snowflake
That uses it to return some statistics about your Google + posts; the app is in thegoogle Play Store and itssource is online
Too.
Registering your app
Most services that do oauth 2.0 authorization want you to register your app, and Google's are no exception. you need to visit thegoogle APIs console, create a project, pick the APIS you want to access off
The services menu, and then hit the API access tab to do the registration. It'll want you to enter your package name; the value of the package attribute of
manifest
Element in your androidmanifest. xml.
Also, it'll want the sha1 signature of the certificate you used to sign your app. anyone who's published apps to Google Play apps knows about keystores and signing. but before you get there, you'll be working with your debug-version apps, which are signed
With a certificate living in ~ /. Android/debug. keystore (password: "android "). fortunately, your computer probably already has a program called "keytool" installed; you can use this to get the signature. for your debug version, a correct incantation is:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -v -list
This will print out the sha1 signature in a nicely labeled easy-to-cut-and-paste form.
This may feel a little klunky, but it's worth it, because some magic is happening. when your app is registered and you generate a token and send it to a service provider, the provider can check with Google, which will confirm that yes, it issued that token,
And give the package name of the app it was issued. those of you who 've done this sort of thing previusly will be wondering about client IDs and API keys, but with this mechanism you don't need them.
Using your token
Suppose you 've registered your app and called googleauthutil. gettoken () and received ed a token. for the purposes of this discussion, let's suppose that it's "missassaugaparnassus42 ". then all you need to do is, when you send off an HTTP request to your service
Provider, include an HTTP header like so:
Authorization: Bearer MissassaugaParnassus42
Then your HTTP gets and posts shoshould just work. you shoshould call googleauthutil. gettoken () to get a token before each set of gets or posts; it's smart about caching things appropriately, and also about dealing with token expiry and refresh.
Once again, as I said at the top, if you're happy using
Google APIs client library for Java, it'll take care of all the client-side stuff; you'll still need to do the developer console app registration.
Otherwise, there's a little bit of coding investment here, but the payoff is pretty big: Secure, authenticated, authorized, service access with a good user experience.