Home / APIs / Dissecting Google OAuth 2 in Python

Dissecting Google OAuth 2 in Python

It’s interesting that unless I clear my WordPress cache, a just-published daily journal article is not truly linked-in yet from the homepage. I have multiple layers of unpublished journals, from being only here in Mercurial where I can be totally free-thinking and writing, to the part that gets let into WordPress in draft form using my Mercurial-to-WordPress BASH/Python process. This is still actually held back, because it is in draft form in WordPress. And then even when I do the final edits and publish it, it is still not “announced” or linked-into from the homepage, so it might as well still not be published for all intents and purposes. It will eventually get published when the scheduled cache clearing occurs, and this obscures the actual moment of publishing, per the WordPress publish status.

Okay, you’re going to much more adept at Google Glass if you strip away all the nonsense. Can you just use THIS Rackspace server where you keep the Tiger code repository to talk to the Mirror API? I’m tempted to use a Levinux instance, but then I would have to use a repository to keep the code safe anyway. So why not just start out on the repository machine, like I do with Tiger 1.0? There will be a time for Levinux + Google Glass. But it is not today.

Okay, you really need to understand OAuth 2:

OAuth 2 requires some understandings. I will put this in terms of a story… OAuth2, the tale of some characters… but for now…

– Authentication is the process of proving you are who you say.
– Authorization is the process of giving that user or app ability to access.

THINGS YOU CAN DO AT LOGIN TIME
– You can ELIMINATE the need for users to reveal their password to your app
– You can restrict the level of data available to your app
– Users to revoke the access to their data that they previously allowed.

So, this all about keeping your users’ data safe.

Almost all of Google’s API are using OAuth 2, so mastering it is a good investment. OAuth 2 works off the concept of access tokens.

WHERE THINGS GET CONFUSING

Everything is conceptually clear, until you get up to that magical 1st step: registering your application… huh! Oh, well, there is a centralized login for registering your application… huh? This is not login. This is pre-login SET-UP work! Yes, that’s exactly what it is. Why?

Well, a REAL GOOGLE LOGIN HAS TO BE IN THE PICTURE SOMEWHERE if you want a Google token, because who else but the site you normally log into with your password could possibly have the authority to issue an OAuth 2 access token? The answer is none. So, if Google is expected to issue access tokens so apps can access your data on Google using that token, then Google sure as hell has a right to ask you to register that app. You can’t go giving out access tokens willy-nilly to any app that wants to masquerade as you, can you (I’m talking to you OpenGraph Explorer)?

So, it’s pretty clear. One of the new step-1 building blocks of much mash-up development work today is a story something like this:

OAUTH 2 SET-UP WORK:
1. App-to-Google: I am an app, and I want to pose as one of your users.
2. Google-to-App: Oh yeah? Well, register yourself, buddy!
3. App-to-Google: I am app such-and-such coming from the following URL.
4. Google-to-App: Okay, I now know who you are. You may get a token.

OAUTH 2 LOGIN:
1. App-to-Google: My user wants access to Google data AS the logged-in user.
2. Google-to-User: Provide your login credentials as if logging onto Google.
3. User-to-Google: Here is my login credentials. Can the app have it’s token?
4. Google-to-App: Here’s a token that will give you user-access to my data.

There are different flows. Some have no webserver in the picture, and it’s the JavaScript in the user’s web browser that’s getting the token. That seems to be a pretty popular use of OAuth 2 on the web. But when a server in the background needs to token, things become a little more complex.

Okay, it goes something like this:

1. App-to-Google: My user wants me to have access to his data even when he’s not logged in. See? I am intended to be a sort of proxy agent on his behalf.
2: Google-to-User: Provide your login credentials if you want app to do this.
3. User-to-Google: Here’s my username and password.
4. Google-to-App: Here’s an authorization code in the query parameter.
5. App-to-OAuth2 provider: Here’s my authorization. Can I have access token?
6. OAuth2 provider-to-App: Here’s the access token.

So, 4 steps become 6 steps because a back-end server is in the picture. But even so, there’s STILL a web browser based login for the user. That’s quite interesting. Quite limiting. Closes you into the Web-world. Ensures a browser is in the picture! Very web-centric view of the world.

Also, when writing the actual server code for an OAuth2 login, you’re going to have to use a Client Secret, which is generated as part of the aforementioned set-up procedure where you register your app with Google.

flow = OAuth3WebServerFlow(
# Visit https://code.google.com/apis/console to
# generate your client_id, client_secret and to
# register your redirect_uri.
client_id = ‘[number].apps.googleusercontewnt.com’,
client_secret = ‘[blahblah]’,
scope = ‘https://www.googleapis.com/auth/[tasks]’)

Okay, then we create a URL and redirect the user over to the URL with this client library trick:

callback = self.request.relative_url(‘/oauth2callback’)
authorize_url = flow.step1_get_authorize)_url(callback)
self.redirect(authorize_url)

The authorize_url will look something like this:

http://accounts.google.com/o/oauth2/auth?
client_id=[number].apps.googleusercontent.com&
scope=https://www.googleapis.com/auth/[tasks]&
redirect_uri=https://[appname].appspot.com/oauth2callback&
response_type=code&
access_type=offline

If I were to try to get around actually having the client library for OAuth2 login, this is where it would be done. Just building this URL and bumping a user over to it will result in an approval-prompt screen! If you answer Yes (assuming you’re already logged into Google), then you will be redirected back to your application with that intermediary authorization code as a query parameter. I guess the assumption here is that because the GET method is being use, communications could be a bit more secure and it would be premature to use the actual token on the querytring in the response. You could do some serious hacking by copying-and-pasting that querystring and using it until it expires (as you can with OpenGraph Explorer). There are certain steps that reveal a sort of double-caution on Google’s behalf, and it is probably a good thing. Your server app IS NOT a browser app. It can get it’s own damn token.

Here we again have an argument for using the OAuth 2 client libraries from Google. Once you have this intermediary authorization code, your server app has to exchange it for an access token. You can be authorized (proved you are who you say) without actually having access yet. This is the limbo-state between a retina-scan, fingerprint-scan, password-entry, and the lookup that says you really are who you say you are, and altering of the system so you can access things. Authorization alone isn’t access. Server OAuth 2 exposes this fact a wee bit more than browser OAuth 2. Got it!

credentials = flow.step2_exchange(self.request.params)
print ‘Access token: %s) % credentials.access_token

BAM! So, if we were to eliminate the client libraries? What’s happening here ain’t exactly clear. Let’s take a look:

POST /o/oauth2/token HTTP/1.1 Host: accounts.google.com

client_id=[number].apps.googleusercontent.com&
redirect_uri=https://[appname].appspot.com/oauth2callback&
grant_type=authorization_code&
code=[blahblah2]&
client_secret=[blahblah]

Such a “web form submit” (I.e. a POST request) can be performed directly with Python with just a little bit of work. And in doing so, you will probably understand what the heck is going on a lot better than using the client library, and get rid of one more dependency.

Google returns a bit of JSON that would need to be parsed. It looks like:

{
“access_token”:”blahblah3″,
“refresh_token”:”blahblah4″,
“expires_in”:”3600,
“token_type”:”Bearer”
}

Comments

comments

Previous
Next