Google OAuth2 API - Howto (mostly)

Date December 19, 2013

Google has an amazing amount of data, and pretty much every project I'm involved in outside of my day job uses Google Drive (nee Docs) and Google Spreadsheets to store immense amounts of information. Looking at a spreadsheet isn't necessarily the best way to take in information, so it's useful to get that data out of it and see it in a way that makes sense to you.

For that reason, I've been writing an environment to rapidly spin up new apps that use the Google Spreadsheet API, and I put it on github.  The code I'm writing is called BootstrapMatrix because it takes a Twitter Bootstrap environment and adds an array of APIs, CSS and javascript libraries on top. It's totally not done yet, but it'll get there.

As an ironic twist, today, my friend (and fellow LOPSA Board member) Ski Kacoroski asked the following question on the LOPSA Discuss list:

Hi,
We have a home grown account management app that was using the Google OAuth1 api to manage accounts in our Google Apps domain.  They have now changed to OAuth2 and things are breaking.  I have been trading emails with Google Support for over a week now with no luck in figuring out how to get a service account for Google OAuth2 working.  The support person keeps directing me to documents that do not match at all with what I see on the Google Console or other screens they direct me to and they refuse to start a webex to simply resolve the problem.

So, has anyone here written an app that uses the service account OAuth2 to connect to a Google API.  If so, I would really appreciate a bit of your time as I am pulling out what little hair I have left over this.

cheers,

ski

I'm new to the whole Google OAuth API thing, but I figured I could write what I've learned in the past week or so, plus it will help cement it in my brain.

I'm going to explain what I've found out as though it's all brand new. Any explanation of things you already know is accidental, so please bear with me.

The Google API console that you use to manage your keys and credentials is here:
https://cloud.google.com/console

Any existing projects that you have there will show up. Click on it, and you'll go to a blank page (at least mine is blank) with a menu on the left:

Click on "APIs & auth" and you'll have subchoices:

Under APIs, all of the various Google services default to "Off". Click the "Off" button to turn them on:

It may take some time, because backend work is being done to generate the necessary credentials for each service. Now, click Credentials on the menu to the left.

At this time, you have one of three choices, depending on how your application needs to access the resources. If you have specific resources that are shared with specific people (such as Google Docs or Google Drive files), then you want to use the OAuth settings at the top:

You can have multiple redirect URIs and javascript origins - they're the places that an application which is using your key is allowed to point people when they sign in. You want to have this be the URL of your application.

It doesn't sound like you're using Public API access, which would be useful if you wanted to embed custom Google Maps on your website or something like that which isn't tied to a particular user account.

The other important thing on that page is at the bottom, "Key for server applications":

Although I haven't seen it referred to by Google anywhere, the "API Key" listed there is what a lot of software and libraries call the "Developer Key", as I make reference to in this file.

Once you have the developer key, the client ID, and the client secret, you're ready to rock and roll.

The general workflow for logging in via OAuth is like this:

1) Instantiate a new instance of Google_Client.
Use the object methods to set the application name, client ID, client secret, redirectURI, and developer key.

2) Set the scopes of the GoogleClient object.

Scopes are a little bit crazy, imo. Google loves APIs. They have so many of them that there's actually an API to query the APIs. You can read about it here: https://developers.google.com/discovery/

In essence, what the scopes do is to say, "here are the permissions that my app needs". On the back end, this is then compared with the APIs you signed up for above (if you didn't request Google Maps API access, but your app is requesting it, things could go badly), and it presents those permissions to the user, as in "Is it okay if this app can use your credentials while you're logged out?".

To be honest, I still haven't figured out exactly how to use the discovery API (maybe someone else can chime in and we can both learn). I used examples from various places, and ended up with code that works, though it's very much cargo cult (I've only been doing this for a few days).

I'm doing this:
$GoogleScopes = array(
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
"https://spreadsheets.google.com/feeds"
);

which pleases the Google Gods and gives me access to the spreadsheets I need, with the caveat that the user accessing them must have read/write access. I'm still not certain why. I am apparently not the only person experiencing this. A friend turned me on to this blog entry by the folks who write Basecamp.

OK, we'll assume that your scopes are now set up, either through luck or magic, and move on to the next step:

3) Authenticate the user 
 
So, when your user shows up fresh and new, they're not going to be authenticated with Google and they're not going to have any session variables on your site (at least, that's what I'm assuming). Your code will need to check to see if they do, in fact, have a session token (or whatever method your code uses of retaining state between page loads). Assuming that they do not have a session token, then you need to display the friendly "Log in via Google!" box(https://developers.google.com/accounts/images/sign-in-with-google.png) and make it a link to $GoogleClient->createAuthUrl().

Remember up above when we said, "Hey $GoogleClient, here's all of our secret stuff"? All of that was so this link could point to somewhere at Google that would absolutely identify our App as the source of this login attempt. The user clicks on the link and gets taken to a Google page which talks about the app they visited, what permissions are being requested, and so on. They have the choice at this time to log in, and if they do, they're sent to the RedirectURI, and appended to that URL is a ?code=XXXXXX string. That XXXXX is their token, and uniquely identifies them.

So in your code, you first need to check if there's an HTTP GET variable named "code". If so, then you call $GoogleClient->authenticate() (note that I don't know exactly what magic this does. I suspect that it is, in fact, authenticating US with google, not the client).

After the authenticate() call, then you set the session token variable (in PHP's case, $_SESSION['token']) to $GoogleClient->getAccessToken(). This ensures that your user can be identified persistently. At this point, you want to redirect your user back to your app (because remember, they're still at /your/app?code=XXXXXX). so inserting a Location header redirection seems to be the right way.

Back to the code that checks for a "code" GET variable. We already saw what happens when 'code' is set. When it isn't set, then you need to check to see if they've got a 'token' session variable (or whatever you decided to call it). If they do, then you use that to set their Google access token using $GoogleClient->setAccessToken($tokenvariable).

At this point, all of the unique stuff for setting up the user's authentication is done, and you're ready to use the API calls to access Google resources.
I know that this isn't a complete guide, but I'm still learning, and honestly, some of it is still outside of my understanding. I hope you got something out of this, though, and maybe BootstrapMatrix can do something useful for you.