While working on one of my project, I had a requirement to access Google calendar’s API, but with constraints that the current user may not be authenticated with OAuth 2.0 and it should always create new event in common calendar. After googling around, I came across the service accounts which allows back-end authentication with Google authorization server.
Basically, Service accounts allows you to access Google API’s using OAuth 2.0 on-behalf your web application instead of showing user consent, so that your application uses service account’s credential to prove its own identity. In my case, I had to use it for accessing Calendar API but you can use it to access any other Google API’s.
To create a new service account in Google Developer’s console, you have to create new client ID with application type as service account. It will create new service account with unique client id, email id and public/private key pair. Note that you should keep your private key securely in development and production environment and don’t ever add it to the source control.
Now, before making any request to access API, you will need to get an access token by using service accounts credential. This request will be POST request with parameters as grant-type and JWT(JSON Web Token) signed with private key.
First parameter is grant type which will be jwt-bearer(it means give access token without refresh token). Second parameter is JWT which contains service account’s credential in encrypted predefined format. Format contains header, claim set and signature. Header contains signing algorithm which is used in signing JWT, claim set contains email address of service account, scope, expiration time etc. Signature is signed composition of header and claim set.
After making request to authorization server, you will receive an access token, which will last until maximum one hour. Using this access token, you can now access Google API same way you access it by using end-user’s credential.

Image source from developers.google.com
Above mechanics requires application to create and cryptographically sign JWT’s, and it’s easy to make serious errors that can have a severe impact on the security of application. Instead, all these things can be done by using client libraries. Ruby contains google_api_client gem for fetching access token, creating requests to access API and parsing the results.
Following code snippet is about how you can access Google Calendar’s API,
# Initialize the client client = Google::APIClient.new(application_name: 'Service account demo', application_version: '0.0.1') # load and decrypt private key key = Google::APIClient::KeyUtils.load_from_pkcs12('path/to/key/file', 'notasecret') # generate request body for authorization client.authorization = Signet::OAuth2::Client.new( :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :audience => 'https://accounts.google.com/o/oauth2/token', :scope => 'https://www.googleapis.com/auth/calendar', :issuer => '123456-abcdef@developer.gserviceaccount.com', :signing_key => key) # fetch access token client.authorization.fetch_access_token! # load API definition service = client.discovered_api('calendar', 'v3') # access API by using client client.execute(...)
If you get error like ‘invalid grant’ while accessing API, check your system time is in sync with NTP or you are using correct public/private key pair.
PS: We can’t login using service account’s email address. In my case, I had to share service account’s calendar with the calendar I was using by inserting an access control rule.
body = { 'role' => 'owner/reader/writer/none', 'scope' => { 'type' => 'default/user/group/domain', 'value' => 'your_account@domain.com' } } client.execute(:api_method => service.acl.insert, :parameters => { 'calendarId' => 'primary' }, :headers => { 'Content-Type' => 'application/json' }, :body => JSON.dump(body))
awesome i spent 1 day to find out how to send body in execute method. But you did a great job i got success in it. Thanks a lot.
LikeLike
I keep getting this error: “Authorization failed. Server message: { “error” : “invalid_request”, “error_description” : “Required parameter is missing: grant_type” }”
LikeLike
Seems like you aren’t passing
grant_type
in parameters while getting access token. Could you comment your parameters ?LikeLike
These are my parameters at the moment. Before i did not have that grant_type parameter as it wasn’t specified to include it, after some googling I found this parameter & value and included it, but I get the same error.
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => ‘https://accounts.google.com/o/oauth2/token’,
:audience => ‘https://accounts.google.com/o/oauth2/token’,
:scope => ‘https://www.googleapis.com/auth/calendar’,
:issuer => ENV[‘GOOGLE_CLIENT_EMAIL’],
:person => ENV[‘GOOGLE_CALENDAR_EMAIL’],
:grant_type => ‘authorization_code’,
:signing_key => key
)
LikeLike
Try passing
jwt-bearer
asgrant_type
as I mentioned above.LikeLike