Read/Write Google sheet via Server without using OAuth - java

Tried going through the internet and google docs they provide OAuth way only. Is there a way to read/write to google sheets with API Key and not OAuth.

After some research, Credential object from google-oath-client module can help. Download the .p12 file from the google account. Code for reading a google sheet without OAUth prompt below. This can also be used to write or append sheets with some modification :
package com.mycomp;
import com.google.api.client.auth.oauth2.Credential;
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.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.sheets.v4.Sheets;
import com.google.api.services.sheets.v4.SheetsScopes;
import com.google.api.services.sheets.v4.model.ValueRange;
import com.nm.vernacular.services.SpreadSheetsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Created by ankushgupta & modified for SO.
*/
public class GoogleSheetsReader {
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final String KEY_FILE_LOCATION = "<Name of p12 file>.p12";
private static final String SERVICE_ACCOUNT_EMAIL = "<email of google service account>";
private static final String APPLICATION_NAME = "Google Sheets API";
private static final Logger LOGGER = LoggerFactory.getLogger(GoogleSheetsReader.class);
/**
* Global instance of the scopes required by this quickstart.
* If modifying these scopes, delete your previously saved credentials/ folder.
*/
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS);
/**
* Creates an authorized Credential object.
* #return An authorized Credential object.
* #throws IOException If there is no client_secret.
*/
private Credential getCredentials() throws URISyntaxException, IOException, GeneralSecurityException {
//Reading Key File
URL fileURL = GoogleSheetsReader.class.getClassLoader().getResource(KEY_FILE_LOCATION);
// Initializes an authorized analytics service object.
if(fileURL==null) {
fileURL = (new File("/resources/"+ KEY_FILE_LOCATION)).toURI().toURL();
}
// Construct a GoogleCredential object with the service account email
// and p12 file downloaded from the developer console.
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
return new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountPrivateKeyFromP12File(new File(fileURL.toURI()))
.setServiceAccountScopes(SCOPES)
.build();
}
#Override
public List<Object[]> readSheet(String nameAndRange, String key, int[] returnRange) throws GeneralSecurityException, IOException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final String spreadsheetId = key;
final String range = nameAndRange;
try {
Sheets service = new Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials())
.setApplicationName(APPLICATION_NAME)
.build();
ValueRange response = service.spreadsheets().values()
.get(spreadsheetId, range)
.execute();
List<List<Object>> values = response.getValues();
int a = returnRange.length;
List<Object[]> result = new LinkedList<>();
if (values == null || values.isEmpty()) {
return Collections.emptyList();
} else {
for (List row : values) {
if(row.size() >= a) {
Object[] objArr = new Object[a];
for(int i=0;i<a;i++) {
objArr[i] = row.get(returnRange[i]);
}
result.add(objArr);
}
}
}
return result;
} catch(Exception ex) {
LOGGER.error("Exception while reading google sheet", ex);
} finally {
}
return null;
}
public static void main(String[] args) {
GoogleSheetsReader reader = new GoogleSheetsReader();
reader.readSheet("<Sheet Name>!A2:B", "<sheets key from URL>", new int[]{0, 1});
}
}

Based from this documentation, when your application requests public data, the request doesn't need to be authorized, but does need to be accompanied by an identifier, such as an API key.
Every request your application sends to the Google Sheets API needs to identify your application to Google. There are two ways to identify your application: using an OAuth 2.0 token (which also authorizes the request) and/or using the application's API key. Here's how to determine which of those options to use:
If the request requires authorization (such as a request for an individual's private data), then the application must provide an OAuth 2.0 token with the request. The application may also provide the API key, but it doesn't have to.
If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you.
However, there are some scopes which require OAuth authorization. Check this link: Access Google spreadsheet API without Oauth token.

Using API key, you can read from google sheets, but only if the sheet is shared with public.
However to write to google sheets, you must you OAuth. See this link.

Related

Authenticate with a Service Account for Google Sheets API with Java

I started with the Google Quick Start guide for Java for connecting to Google Sheets via their API (https://developers.google.com/sheets/api/quickstart/java). I am following their quick start tutorial as it is without any changes. However, instead of creating an OAuth client ID and downloading that JSON credentials file, I created a Service Account (because that's what it's going to be). However, the downloaded service account JSON file does not work with Google's Quick Start code :
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.sheets.v4.Sheets;
import com.google.api.services.sheets.v4.SheetsScopes;
import com.google.api.services.sheets.v4.model.ValueRange;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
public class SheetsQuickstart {
private static final String APPLICATION_NAME = "Google Sheets API Java Quickstart";
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final String TOKENS_DIRECTORY_PATH = "tokens";
/**
* Global instance of the scopes required by this quickstart.
* If modifying these scopes, delete your previously saved tokens/ folder.
*/
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS_READONLY);
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
/**
* Creates an authorized Credential object.
* #param HTTP_TRANSPORT The network HTTP Transport.
* #return An authorized Credential object.
* #throws IOException If the credentials.json file cannot be found.
*/
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStream in = SheetsQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
/**
* Prints the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
public static void main(String... args) throws IOException, GeneralSecurityException {
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
final String range = "Class Data!A2:E";
Sheets service = new Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
ValueRange response = service.spreadsheets().values()
.get(spreadsheetId, range)
.execute();
List<List<Object>> values = response.getValues();
if (values == null || values.isEmpty()) {
System.out.println("No data found.");
} else {
System.out.println("Name, Major");
for (List row : values) {
// Print columns A and E, which correspond to indices 0 and 4.
System.out.printf("%s, %s\n", row.get(0), row.get(4));
}
}
}
}
and it gives this exception:
Task :run FAILED
Exception in thread "main" java.lang.IllegalArgumentException
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:131)
at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:35)
at com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.getDetails(GoogleClientSecrets.java:80)
at com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow$Builder.(GoogleAuthorizationCodeFlow.java:211)
at SheetsQuickstart.getCredentials(SheetsQuickstart.java:50)
at SheetsQuickstart.main(SheetsQuickstart.java:68)
I have already given 'Edit' access to the service account email id given in the json file to my Google spreadsheet, but it still gives the same error (in fact I tried changing my file permissions to 'anyone with link' can edit, and have used the same google spreadsheet ID in the code). Some webpages suggested a deprecated GoogleCredential based solution. Is there a non-deprecated solution to the problem?

Google Drive API Java, Authentication works for first time only

I use following code to upload file to google drive, it is used for java based web application. Everything works fine except authentication.
The main problem with the code is that it just ask once for authentication and later on when I run the project, it never ask for authentication.
I want authentication for each and every request made by client.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package myapp.util;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.media.MediaHttpUploader;
import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.InputStreamContent;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.*;
import com.google.api.services.drive.Drive;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
public class GDrive {
/**
* Application name.
*/
private static final String APPLICATION_NAME
= "Drive API Java Quickstart";
/**
* Directory to store user credentials for this application.
*/
private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".credentials/drive-java-quickstart.json");
/**
* Global instance of the {#link FileDataStoreFactory}.
*/
private static FileDataStoreFactory DATA_STORE_FACTORY;
/**
* Global instance of the JSON factory.
*/
private static final JsonFactory JSON_FACTORY
= JacksonFactory.getDefaultInstance();
/**
* Global instance of the HTTP transport.
*/
private static HttpTransport HTTP_TRANSPORT;
/**
* Global instance of the scopes required by this quickstart.
*
* If modifying these scopes, delete your previously saved credentials at
* ~/.credentials/drive-java-quickstart
*/
private static final List<String> SCOPES
= Arrays.asList(DriveScopes.DRIVE_FILE);
static {
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}
/**
* Creates an authorized Credential object.
*
* #return an authorized Credential object.
* #throws IOException
*/
public static Credential authorize() throws IOException {
ServletContext servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
String basePath = servletContext.getRealPath("");
basePath = URLDecoder.decode(basePath, "UTF-8");
// Load client secrets.
InputStream in = new FileInputStream(
new java.io.File(basePath + "/resources/client_secret.json"));
GoogleClientSecrets clientSecrets
= GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow
= new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.setApprovalPrompt("auto")
.build();
GoogleCredential credential = new GoogleCredential();
System.out.println(
"Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
/**
* Build and return an authorized Drive client service.
*
* #return an authorized Drive client service
* #throws IOException
*/
public static Drive getDriveService() throws IOException {
Credential credential = authorize();
System.out.println("getAccessToken" + credential.getAccessToken());
System.out.println("setAccessToken" + credential.getAccessToken());
System.out.println("setExpiresInSeconds" + credential.getExpiresInSeconds());
System.out.println("setRefreshToken" + credential.getRefreshToken());
return new Drive.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
public boolean uploadToDrive(String filePathUrl, String fileName) throws FileNotFoundException, IOException, GeneralSecurityException, Exception {
Drive service = GDrive.getDriveService();
boolean status = false;
// TODO code application logic here
java.io.File mediaFile = new java.io.File(filePathUrl);
com.google.api.services.drive.model.File fileMetadata = new com.google.api.services.drive.model.File();
fileMetadata.setName(fileName);
InputStreamContent mediaContent = new InputStreamContent("application/octet-stream", new BufferedInputStream(new FileInputStream(mediaFile)));
mediaContent.setLength(mediaFile.length());
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Drive.Files.Create request = service.files().create(fileMetadata, mediaContent);
request.getMediaHttpUploader().setProgressListener(new CustomProgressListener());
request.execute();
status = true;
return status;
}
}
class CustomProgressListener implements MediaHttpUploaderProgressListener {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation has started!");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation is complete!");
break;
case MEDIA_IN_PROGRESS:
System.out.println(uploader.getProgress());
break;
case MEDIA_COMPLETE:
System.out.println("Upload is complete!");
}
}
}
By design the client library stores authorization information, so that the user isn't prompted every time they run your application. The client library persists authorization information to the DataStore instance you provide, in your case a FileDataStore which stores files in a particular directory.
Multiple users can share the same data store, but you need to pass a unique user identifier when performing authorization. For installed applications this is done in AuthorizationCodeInstalledApp.authorize, and for web applications it's done by overriding AbstractAuthorizationCodeServlet.getUserId and AbstractAuthorizationCodeCallbackServlet.getUserId. See the Java client library's OAuth2 guide for more information and examples.

Authorization to Google Client

I am writing a class for creating authorization to BigQuery and Google Cloud Storage.
In the past I have used CredentialStore which has been deprecated. I am trying to use DataStoreFactory but I discovered that it allows me to use only StoredCredential while I need a Credential.
I know one can convert from Credential to StoredCredential but I am not sure how to convert them in the opposite direction (StoredCredential to Credential). For example I am creating my connection like this:
Storage.Builder(HttpTransport transport,
JsonFactory jsonFactory,
HttpRequestInitializer httpRequestInitializer);
Could anyone point me in a direction about how to achieve this?
Thank you!
In most cases, wherever you use Credential, you could use StoredCredential. There is only one point you would work with Credential, which is retrieving the Access Token during the OAuth callback. From there the Credential can be converted to StoreCredential and stored into the DataStore. After that storage and retrieval all works with StoredCredential.
But there are places were StoredCredential can't be used. I just encountered one trying to create the Google Drive API Service wrapper.
There is a way to get around this with the GoogleCredential object, it can be created from StoredCredential as per this answer:
Stored Credential from Google API to be reused using Java
import com.google.api.client.auth.oauth2.StoredCredential;
public static GoogleCredential createGoogleCredential(StoredCredential storedCredential) {
GoogleCredential googleCredential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport())
.setJsonFactory(new JacksonFactory())
.setClientSecrets("client_id", "client_secret")
.setAccessToken(storedCredential.getAccessToken())
.build();
return googleCredential;
}
I'm using google-oauth-client-1.22.0.jar.
I have a Java server with static Service Account credentials that authenticates with Google Cloud.
Here is the salient code that I use.
The key here is to add a CredentialRefreshListener which, when you successfully authenticate, then the Google OAuth client library will call and give you a StoredCredential object which you can serialize and store. You store it, and retrieve it, to a location of your choice by writing or instantiating an implemenation of the DataStore interface. Your DataStore implementation is constructed using a DataStoreFactory implementation.
After building my GoogleCredential, then I can read the last access token, refresh token and expiration date from my DataStore.
If the access token isn't about to expire, then the Google client API will use it to log in.
When it's about to expire, or this is the first time this code is called, then the Google Client API will invoke the refresh listener and it will store the first access token, refresh token and expiration date.
import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener;
import com.google.api.client.auth.oauth2.StoredCredential;
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.client.util.IOUtils;
import com.google.api.client.util.store.AbstractDataStore;
import com.google.api.client.util.store.AbstractDataStoreFactory;
import com.google.api.client.util.store.DataStore;
import com.google.api.client.util.store.DataStoreFactory;
import com.google.api.services.storage.Storage;
import com.google.api.services.storage.StorageScopes;
import com.google.api.services.storage.model.StorageObject;
import java.io.IOException;
import java.io.Serializable;
import java.util.Base64;
public class StackOverflow {
public static void main(final String... args) throws Exception {
final String clientEmail = "todd.snider#aimless.com";
final HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
final JsonFactory jsonFactory = new JacksonFactory();
// This implementation generates an that stores and retrieves StoredCredential objects
final DataStoreFactory dataStoreFactory = new AbstractDataStoreFactory() {
#Override
protected <V extends Serializable> DataStore<V> createDataStore(final String id) {
return new MyDataStore<>(this, id);
}
};
// construct a GoogleCredential object to access Google Cloud
final GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(transport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(clientEmail)
.setServiceAccountScopes(StorageScopes.all())
.setServiceAccountPrivateKey(readEncryptedPemFile())
.setServiceAccountPrivateKeyId("___static_get_this_from_google_console___")
.addRefreshListener(new DataStoreCredentialRefreshListener(clientEmail, dataStoreFactory))
.build();
// See I have an access token, refresh token and expiration date stored in my DataStore.
// If so, set them on the GoogleCredential
final StoredCredential storedCredential = StoredCredential.getDefaultDataStore(dataStoreFactory).get(clientEmail);
if (storedCredential != null) {
credential.setAccessToken(storedCredential.getAccessToken());
credential.setRefreshToken(storedCredential.getRefreshToken());
credential.setExpirationTimeMilliseconds(storedCredential.getExpirationTimeMilliseconds());
}
// Now I can use Google Cloud
final Storage storage = new Storage.Builder(credential.getTransport(), credential.getJsonFactory(), credential).setApplicationName("Aimless").build();
storage.objects().insert("soem bucket", new StorageObject());
}
private static class MyDataStore<V extends Serializable> extends AbstractDataStore<V> {
MyDataStore(DataStoreFactory dataStoreFactory1, String id1) {
super(dataStoreFactory1, id1);
}
#Override
public DataStore<V> set(String key, V value) throws IOException {
final String encoded = Base64.getEncoder().encodeToString(IOUtils.serialize(value));
db.save(key, encoded);
return this;
}
#Override
public V get(String key) throws IOException {
final String encoded = db.get(key);
if (encoded == null) {
return null;
}
return IOUtils.deserialize(Base64.getDecoder().decode(encoded));
}
// etc.
}

Display the list of files using Google Drive SDK API in Java

I can not fix...below you can find the code:
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class getDriveService {
/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL = "<email_address_service>#developer.gserviceaccount.com";
/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "src/<file_key>-privatekey.p12";
public static void main(String[] args) throws IOException, GeneralSecurityException, URISyntaxException {
List<File> lista = null;
lista = retrieveAllFiles(getDriveService());
System.out.println(""+lista.size());
for (File file : lista) {
System.out.println(""+file.getId());
}
}
/**
* Build and returns a Drive service object authorized with the service accounts
* that act on behalf of the given user.
*
* #param userEmail The email of the user.
* #return Drive service object that is ready to make requests.
*/
public static Drive getDriveService() throws GeneralSecurityException,
IOException, URISyntaxException {
Collection<String> elenco = new ArrayList<String>();
elenco.add("https://www.googleapis.com/auth/drive");
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(elenco)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
.setApplicationName("FileListAccessProject")
.setHttpRequestInitializer(credential).build();
return service;
}
/**
* Retrieve a list of File resources.
*
* #param service Drive API service instance.
* #return List of File resources.
*/
private static List<File> retrieveAllFiles(Drive service) throws IOException {
List<File> result = new ArrayList<File>();
Files.List request = service.files().list();
do {
try {
FileList files = request.execute();
result.addAll(files.getItems());
request.setPageToken(files.getNextPageToken());
} catch (IOException e) {
System.out.println("An error occurred: " + e);
request.setPageToken(null);
}
} while (request.getPageToken() != null &&
request.getPageToken().length() > 0);
return result;
}
}
If I run the main method, it display "0". The file list is empty. Why?
The code seem right, I also enabled the service "Drive API" from the Google APIs Console panel ... and I left disabled "Drive SDK" (which I do not understand why, but here https://developers.google.com/drive/delegation says so)
What could be wrong? Did I forget something?
Thanks in advance for any help
Francesco
You must set ServiceAccountUser as the user's drive you wanna access, and ServiceAccountId as the Service Account User
something like this:
GoogleCredential credentialUser = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountUser("user#gmail.com")
.setServiceAccountPrivateKeyFromP12File(new File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.setServiceAccountScopes(serviceAccountScopes).build();

BigQuery and OAuth2

I'm trying to access Google BigQuery using Service Account approach. My code is as follows:
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleCredential credentials = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("XXXXX#developer.gserviceaccount.com")
.setServiceAccountScopes(BigqueryScopes.BIGQUERY)
.setServiceAccountPrivateKeyFromP12File(
new File("PATH-TO-privatekey.p12"))
.build();
Bigquery bigquery = Bigquery.builder(HTTP_TRANSPORT, JSON_FACTORY).setHttpRequestInitializer(credentials)
.build();
com.google.api.services.bigquery.Bigquery.Datasets.List datasetRequest = bigquery.datasets().list(
"PROJECT_ID");
DatasetList datasetList = datasetRequest.execute();
if (datasetList.getDatasets() != null) {
java.util.List<Datasets> datasets = datasetList.getDatasets();
System.out.println("Available datasets\n----------------");
for (Datasets dataset : datasets) {
System.out.format("%s\n", dataset.getDatasetReference().getDatasetId());
}
}
But it throws the following exception:
Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Authorization required",
"reason" : "required"
} ],
"message" : "Authorization required"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:159)
at com.google.api.client.googleapis.json.GoogleJsonResponseException.execute(GoogleJsonResponseException.java:187)
at com.google.api.client.googleapis.services.GoogleClient.executeUnparsed(GoogleClient.java:115)
at com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.java:112)
at com.google.api.services.bigquery.Bigquery$Datasets$List.execute(Bigquery.java:979)
The exception is fired on this line:
DatasetList datasetList = datasetRequest.execute();
I'm getting the account ID from Google's API console from the second line on the section that looks like this:
Client ID: XXXXX.apps.googleusercontent.com
Email address: XXXXX#developer.gserviceaccount.com
What am I missing?
Eureka! Both Eric's and Michael's code works well.
The error posted in the question can be reproduced by setting the time on the client machine incorrectly. Fortunately, it can be solved by setting the time on the client machine correctly.
Note: For what it's worth, I synchronized the time on a Windows 7 box using the "Update now" button in the "Internet Time Settings" dialog. I figured that should be pretty idiot-proof... but I guess I beat the system. It corrected the seconds but left the machine off by exactly one minute. The BigQuery call failed after that. It succeeded after I manually changed the time.
Our error handling code in the Java library needs to be improved a bit!
It looks like the signed JWT for requesting an OAuth access token is failing. You can see this by enabling the logs that #MichaelManoochehri mentioned above.
There's only a few things that I think could be causing this failure:
Invalid signature (using the wrong key)
Invalid e-mail address for the service account (I think that's been ruled out)
Invalid date/time stamp used for generating the signed blob (an issue date, and an expiration date)
Invalid scope (I think that's been ruled out)
You should check that your date/time is properly set on your server with the proper timezone -- sync'd to NTP. You can use time.gov to see the official US atomic clock time.
EDIT: The answer I gave below is relevant to using Google App Engine Service Accounts - leaving here for reference.
Double check that you have added your service account address to your project's team page as an owner.
I'd recommend using the AppIdentityCredential class to handle service account auth. Here's a small snippet that demonstrates this, and I'll add additional documentation about this on the BigQuery API developer page.
Also, make sure that you are using the latest version of the Google Java API client (as of today, it's version "v2-rev5-1.5.0-beta" here).
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpRequest;
import com.google.api.client.http.json.JsonHttpRequestInitializer;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.BigqueryRequest;
#SuppressWarnings("serial")
public class Bigquery_service_accounts_demoServlet<TRANSPORT> extends HttpServlet {
// ENTER YOUR PROJECT ID HERE
private static final String PROJECT_ID = "";
private static final HttpTransport TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static final String BIGQUERY_SCOPE = "https://www.googleapis.com/auth/bigquery";
AppIdentityCredential credential = new AppIdentityCredential(BIGQUERY_SCOPE);
Bigquery bigquery = Bigquery.builder(TRANSPORT,JSON_FACTORY)
.setHttpRequestInitializer(credential)
.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
public void initialize(JsonHttpRequest request) {
BigqueryRequest bigqueryRequest = (BigqueryRequest) request;
bigqueryRequest.setPrettyPrint(true);
}
}).build();
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
resp.getWriter().println(bigquery.datasets()
.list(PROJECT_ID)
.execute().toString());
}
}
Here is a complete snippet for reference:
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.Bigquery.Datasets;
import com.google.api.services.bigquery.model.DatasetList;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
public class BigQueryJavaServiceAccount {
private static final String SCOPE = "https://www.googleapis.com/auth/bigquery";
private static final HttpTransport TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
public static void main(String[] args) throws IOException, GeneralSecurityException {
GoogleCredential credential = new GoogleCredential.Builder().setTransport(TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("XXXXXXX#developer.gserviceaccount.com")
.setServiceAccountScopes(SCOPE)
.setServiceAccountPrivateKeyFromP12File(new File("my_file.p12"))
.build();
Bigquery bigquery = Bigquery.builder(TRANSPORT, JSON_FACTORY)
.setApplicationName("Google-BigQuery-App/1.0")
.setHttpRequestInitializer(credential).build();
Datasets.List datasetRequest = bigquery.datasets().list("publicdata");
DatasetList datasetList = datasetRequest.execute();
System.out.format("%s\n", datasetList.toPrettyString());
}

Categories