Understanding Service Accounts
Misconceptions about Google service accounts are at the heart of a number of problems I’ve seen developers having on Stack Overflow and various issue trackers. Hopefully this post will dispel some common misunderstandings, and break down what they are for.
What is a service account for?
Requests to many APIs need to be authorised to access data or services. In most cases this is done interactively with a user - the site presents a sign-in button, the user grants the site access to a part of their Google account, and the site receives an access token they can pass with their requests. Google checks this token to ensure the query is allowed to access the data it is requesting.
How do service accounts work?A service account is a sort of virtual user or robot account associated with a given project. It has an email address, and a cryptographic key pair. Google keeps one part of the key, and the developer keeps the other. To authenticate, the developer creates a request for an access token and signs it using the key. The request contains information on the service that the robot account wants to access. Google’s auth services process this request to check the service account should be able to access those things, verify the signature, and if all is well return an access token.
This access token authenticates a request as being from the service account user. This is the step that causes problems for many people - because they created the service account in their Google Developer Console project, they assume that the tokens that the service account retrieves represent them - but it doesn't, it represents this virtual user and all Google knows about it is that it is a member of a certain console project. In most cases, that virtual user needs to be granted specific access to anything you want it to be able to retrieve.
Accessing data and services.
For APIs that can be used without authentication, such as the Google+ search API, authenticating with a service account will just associate the call with your Developer Console project. This can be valuable, as it allows increased quota over and above the anonymous access, but you could equally use a Public API access key, which doesn’t require retrieving a token.
However, in most cases the service account is useful only if it can access some user data - but it itself doesn’t have any. The solution is delegation: granting access to the service account to specific user data. The easiest way to see this is to walk through some scenarios. Note that these things do change over time, so if you’re reading this some time after it was written things might be in a different place, but hopefully the general approach should still work.
Creating a service accountVisit the Google Developers Console and select (or create!) the project you want to use. Then in APIs & auth go to the Credentials option. Select Create Client ID, and select type Service Account.
When you click OK, you’ll download a .p12 file in your browser, and see an entry with an email address in it, something @developer.gserviceaccount.com. The .p12 file is your part of the cryptographic key, so treat it as a secret, and don't lose it! You can generate a new one from the console, but you can't download this one again.
Next you need to enable access to any APIs the service account wants to use. This doesn't give you access to user data, instead just enabling the account to query those APIs at all. Under APIs & auth go to Apis and switch on the services you need - if you're following along with this post choose the Analytics API and Drive API.
Now we can look at how you actually grant service accounts access to a couple of different sets of user data.
Accessing Google Analytics data
The Google Analytics API offers you pretty much complete access to the stats about your site or app, but a service account wont be able to see any of it unless you have granted it access. With analytics this is done by adding the service account email address as a user to your Analytics account. Simply access Analytics, go to the Administration > Property Permissions section and select the User Management settings. At the bottom you’ll see an Add permissions for box, which takes an email address. Add your service account email address from the developer console here.
Once that's done, we can retrieve an access token for the service account user, using the
https://www.googleapis.com/auth/analytics.readonly scope, and query the API just as we would if we had a user signed in via the regular OAuth 2.0 flow. In this case we aren't pretending to be a regular Google user at all - we've specifically granted our robot account access to view our analytics data.
This code is PHP, but the flow should be similar with any of the Google API client libraries. Walking through it, we:
- Setup the values for our service account - its email address and the location of the p12 file primarily.
- Load in the key file and create an assertion for the Google Analytics scope.
- Call refreshTokenWithAssertion to get a new access token.
- Query the API.
It is worth noting that the call to get an access token is a little expensive - if you're going to be making multiple calls definitely cache the access token, and only refresh it with the service account when it expires.
Access Google Drive for a Google Apps User
Google Apps domains are designed to be administered through a variety of tools, so come with a more flexible delegation model than just approving the email address like in Analytics. You can actually approve a service account to act on behalf of the users of a Google Apps domain with a predefined set of scopes via the Apps admin console (you can read more about it on the Google Drive delegation page).
Adding an account is done through the Admin page under Security - Advanced Settings (under More) - Manage Api Client Access. To allow access you enter the service account client id (not the email address) and the scopes you wish to access. In our case that's the
We also have to take an extra step in our code and specify precisely which account we want to access, as it could now be any valid user under the apps domain. We use the
sub (subject) parameter on the assertion credential to indicate this by setting it to the email address or the Google user ID of the user we want to act on behalf of. Take a look at the following snippet as compared to the analytics one above - other than the service and scope the only difference is that additional sub parameter.
As a follow up to the note about caching above, be aware that the tokens will be different depending on the sub and scopes requested, even if they're from the same service account. For example: if you cached the token from the Analytics query and then the token from the Drive query, they would be different and not have access to each others resources. Be sure to include the sub, scopes parameter, and service account email address in any cache key generation you do.
Service accounts pop up in all sorts of places, each with their own delegation methods. For example, Google Cloud services have automatic service accounts made available in Compute Engine and App Engine to make querying the Cloud APIs easier. In this case you may grant the scopes via the gcloud command line tool and retrieve the access token via calling a special endpoint. In general with the Cloud services you do not have to worry about delegating access as the data they query is generally owned by the project (for example with a service like Cloud Storage) rather than a specific user.
Hopefully this has made the service account idea a little clearer - they're a powerful tool for background or batch access to APIs, but can easily be confusing (like most auth related topics). If you’re having trouble with one make sure you have correctly configured access for the service account in the admin controls of the relevant API, and that you have requested the appropriate scopes and subject when creating the token request assertion credential.