I have an application in the Google App Engine (hosted on appspot.com) that several users will use to grab some things from the Internet, store it in a data store, and then write it to a Google spreadsheet. I want to use a service account to access a spreadsheet, but I ran into 400 OK {"error": "invalid_grant"} when I try to do something, regardless of whether I try to create a file using the API drive interface or access to an existing file using table APIs.
I turned on the Drive API and Drive SDK in the API console and generated a P12 key. This is my method of building GoogleCredential :
private GoogleCredential getGoogleCredential() throws IOException, GeneralSecurityException { GoogleCredential credential = new GoogleCredential.Builder() .setTransport(TRANSPORT) .setJsonFactory(FACTORY) .setServiceAccountId("1511XXX90247.apps.googleusercontent.com") .setServiceAccountScopes(scopes) .setServiceAccountPrivateKeyFromP12File( new File("05a605b0fXXXd2a6be864be15d81a2bd629d3bd6-privatekey.p12")) .setServiceAccountUser(" XXX@gmail.com ")
ServiceAccountID comes from the API console. I tried using 1511XXX90247@developer.gserviceaccount.com as my ServiceAccountUser as well as myappname@appspot.gserviceaccount.com . The areas I use are as follows:
https://spreadsheets.google.com/feeds https://docs.google.com/feeds https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file
Here is the code that fails when I try to use the spreadsheet API:
SpreadsheetService service = new SpreadsheetService("name of my app"); service.setOAuth2Credentials(getGoogleCredential()); FeedURLFactory factory = FeedURLFactory.getDefault(); SpreadsheetQuery query = new SpreadsheetQuery(factory.getSpreadsheetsFeedUrl()); query.setTitleQuery("test"); SpreadsheetFeed feed = service.query(query, SpreadsheetFeed.class);
The code does not work when the service tries to fulfill the request.
Here the code of the disk I tried also does not work:
Drive drive = new Drive.Builder(TRANSPORT, FACTORY, getGoogleCredential()).build(); com.google.api.services.drive.model.File file = new com.google.api.services.drive.model.File(); file.setTitle("test"); file.setMimeType("application/vnd.google-apps.spreadsheet"); drive.files().insert(file).execute();
No matter what I try, I always seem to get the same "failure" error. Here's the partial stacktrace:
com.google.gdata.util.AuthenticationException: Failed to refresh access token: 400 OK { "error" : "invalid_grant" } at com.google.gdata.client.GoogleAuthTokenFactory$OAuth2Token.refreshToken(GoogleAuthTokenFactory.java:260) at com.google.gdata.client.GoogleAuthTokenFactory.handleSessionExpiredException(GoogleAuthTokenFactory.java:702) at com.google.gdata.client.GoogleService.handleSessionExpiredException(GoogleService.java:738) at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:680) at com.google.gdata.client.Service.query(Service.java:1237) at com.google.gdata.client.Service.query(Service.java:1178) at spam.gwt.scraper.server.spreadsheets.APIConnector.doGet(APIConnector.java:66)
I tried to search for answers everywhere, including, but not limited to, the Google Spreadsheet Design Guide , this is the Account Stack Overflow Question and this stack overflow question in the App Engine application . That with developments (for example, Docs → Drive) it is difficult to find up-to-date information without paying attention to information specific to GAE.