Question

I'm trying to get my Groovy/Grails application to speak to the Google Calendar API from Java, using a service model. For now I'll be happy with a simple calendar list. No matter what I do though, I end up with:

URI: /test
Class: com.google.api.client.auth.oauth2.TokenResponseException
Message: 400 Bad Request { "error" : "invalid_grant" }

The same user "googleaccounts@quirk.biz" owns both the calendar and the API Console App. Code as follows:

package quirkplanner

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.HttpTransport
import com.google.api.client.json.JsonFactory
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.calendar.Calendar
import com.google.api.services.calendar.model.*;

class TestController {

    def servletContext

    private static final HttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    private static final JsonFactory JSON_FACTORY = new JacksonFactory();

    private static final String SERVICE_EMAIL = "12345678901234-abcdefabcdef@developer.gserviceaccount.com";
    private static final String CLIENT_ID = "12345678901234.apps.googleusercontent.com";
    private static final String PATH_TO_CERTIFICATE = "/WEB-INF/abcdefabcdef-privatekey.p12";
    private static final String SERVICE_ACCOUNT_USER="my-google-admin-user@quirk.biz";

    def index() {

        GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
            .setJsonFactory(JSON_FACTORY)
            .setServiceAccountId(SERVICE_EMAIL)
            .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/calendar"))
            .setServiceAccountPrivateKeyFromP12File(new File(servletContext.getRealPath(PATH_TO_CERTIFICATE)))
            .setServiceAccountUser(SERVICE_ACCOUNT_USER)
            .build();

        Calendar service3 = new Calendar(HTTP_TRANSPORT, JSON_FACTORY, credential);
        com.google.api.services.calendar.model.Calendar calendar = service3.calendars().get("primary").execute();

        render("ok")
    }
}

BuildConfig.groovy Dependencies are:

    compile 'com.google.api-client:google-api-client:1.18.0-rc'
    compile 'com.google.http-client:google-http-client-jackson2:1.18.0-rc'

And I also have a list of JAR's I'm using. The list it not complete, hence the BuildConfig dependencies. Ideally, I should really choose one or the other...

Image of the Grails lib folder

Was it helpful?

Solution

There are a few things that could try:

  1. Drop and re-create your private keys in the Google Console.
  2. Share one of the calendars from within the Google Calendar of the service account owner (googleaccounts@quirk.biz) with the SERVICE_EMAIL address.
  3. Do not try to impersonate an account. Remove setServiceAccountUser(SERVICE_ACCOUNT_USER)

Be sure to use the SERVICE_EMAIL for the

.setServiceAccountId(SERVICE_EMAIL)

as you have correctly done. Google's documentation is not clear on this but using the Service Account ID will not work.

Good Luck!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top