5

I'm attempting to connect to Google Compute Engine using Java, but getting an exception that doesn't mean much to me.

The example I'm following says the following:

/** Authorizes the installed application to access user's protected data. */
private static Credential authorize() throws Exception {
  // load client secrets
  GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
      new InputStreamReader(CalendarSample.class.getResourceAsStream("/client_secrets.json")));
  // set up authorization code flow
  GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
      httpTransport, JSON_FACTORY, clientSecrets,
      Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(dataStoreFactory)
      .build();
  // authorize
  return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
} 

In order to get the json file containing the credentials, I go to cloud.google.com, go to my app in the console and click Credentials, I click Create new ClientId, select Service Account and JSON Key.

This downloads a _________.json file.

In a public static void main(String ... args) throws Exception { I have the following code to read the credentials file:

GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
                new FileReader("________9f.json"));

Doing a System.out.println(clientSecrets); prints out the whole json file which contains keys private_key_id, client_email, client_id and type.

Now if I continue with the example code:

// set up authorization code flow
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        httpTransport, JSON_FACTORY, clientSecrets,
                    Collections.singleton(ComputeScopes.COMPUTE)).setDataStoreFactory(dataStoreFactory).build();

// authorize
new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

This gives me the following stacktrace:

Exception in thread "main" java.lang.IllegalArgumentException at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:76) at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37) at com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.getDetails(GoogleClientSecrets.java:82) at com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow$Builder.(GoogleAuthorizationCodeFlow.java:195) at com.mycee.TestGoogle.main(TestGoogle.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

The Missing variables (which are all static variables for now) are as follow:

JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/compute_engine_sample");

I'm trying to manage my Google Compute Engine instances via Java, any idea what I'm doing wrong with the Oath authentication?

Update:

pom.xml as requested:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jvaas</groupId>
    <artifactId>jvaas-cloud</artifactId>
    <packaging>war</packaging>
    <version>1.0.0</version>
    <name>jVaaS Cloud</name>
    <properties>
        <jclouds.version>1.9.0</jclouds.version>
        <project.http.version>1.19.0</project.http.version>
        <project.oauth.version>1.19.0</project.oauth.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.google.http-client</groupId>
            <artifactId>google-http-client-jackson2</artifactId>
            <version>${project.http.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client-jetty</artifactId>
            <version>${project.oauth.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-compute</artifactId>
            <version>v1-rev27-1.19.0</version>
        </dependency>
    </dependencies>
</project>

TestGoogle.java in the default package (kots.json is sitting in src/main/resources):

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.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.compute.Compute;
import com.google.api.services.compute.ComputeScopes;
import com.google.api.services.compute.model.Instance;
import com.google.api.services.compute.model.InstanceList;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class TestGoogle {

    private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/compute_engine_sample");
    private static FileDataStoreFactory dataStoreFactory;
    private static HttpTransport httpTransport;
    private static final String zoneName = "us-central1-a";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final List<String> SCOPES = Arrays.asList(ComputeScopes.COMPUTE_READONLY);

    public static void main(String... args) throws Exception {

        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);

        InputStream in = TestGoogle.class.getResourceAsStream("/kots.json");

        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
        GoogleAuthorizationCodeFlow flow = // <- fails here
                new GoogleAuthorizationCodeFlow.Builder(
                        httpTransport, JSON_FACTORY, clientSecrets, SCOPES)
                        .setDataStoreFactory(dataStoreFactory)
                        .setAccessType("online").setApprovalPrompt("auto")
                        .build();

        new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

    }
}

Full stacktrace:

Exception in thread "main" java.lang.IllegalArgumentException
    at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(Preconditions.java:76)
    at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37)
    at com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.getDetails(GoogleClientSecrets.java:82)
    at com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow$Builder.<init>(GoogleAuthorizationCodeFlow.java:195)
    at TestGoogle.main(TestGoogle.java:38)

Decompiling the relevant class files these are the code snippets related:

GoogleAuthorizationCodeFlow.java: 195

public Builder(HttpTransport transport, JsonFactory jsonFactory, GoogleClientSecrets clientSecrets, Collection<String> scopes) {
    super(BearerToken.authorizationHeaderAccessMethod(), transport, jsonFactory, new GenericUrl("https://accounts.google.com/o/oauth2/token"), new ClientParametersAuthentication(clientSecrets.getDetails().getClientId(), clientSecrets.getDetails().getClientSecret()), clientSecrets.getDetails().getClientId(), "https://accounts.google.com/o/oauth2/auth");
    this.setScopes(scopes);
}

GoogleClientSecrets.java: 82

public GoogleClientSecrets.Details getDetails() {
    Preconditions.checkArgument(this.web == null != (this.installed == null));
    return this.web == null?this.installed:this.web;
}

Preconditions.java: 37

public static void checkArgument(boolean expression) {
    com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument(expression);
}

Preconditions.java: 76

public static void checkArgument(boolean expression) {
    if(!expression) {
        throw new IllegalArgumentException();
    }
}

kots.json with all sensitive data masked out:

{
  "private_key_id": "________________________________________",
  "private_key": "-----BEGIN PRIVATE KEY-----\n__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________\n-----END PRIVATE KEY-----\n",
  "client_email": "_____________________________________________@developer.gserviceaccount.com",
  "client_id": "_____________________________________________.apps.googleusercontent.com",
  "type": "service_account"
}

kots.json was generated when I click on this button in cloud.google.com

enter image description here

Update, seems my json file was incorrect, this format fixed it for me (copied from the conversation with @We are Borg):

{"installed": {
    "client_id": "yourid",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_email": "",
    "client_x509_cert_url": "",
    "client_secret": "yoursecret",
    "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob", "http://tooltank.de"]
}}

Correct place to download it from is to create a new client id and select isntalled application.

Jan Vladimir Mostert
  • 12,380
  • 15
  • 80
  • 137
  • So, what is the original kots.json used for? (I mean, it is the default one that auto-downloads when you create a client, so I assumed it would be useful somewhere?) – CasualT Aug 06 '15 at 21:38
  • 1
    I have yet to find out what it's for, but there's another option that will download the json file in the correct format as shown in my Update. – Jan Vladimir Mostert Aug 07 '15 at 05:45
  • In my case I'm using a service account, so it doesn't quite work for me. – CasualT Aug 07 '15 at 17:51
  • i thought I was using a service account too, turns out I was wrong. Not sure what the service account option is for, the json file it downloads doesn't work according to any of the tutorials I've followed. – Jan Vladimir Mostert Aug 08 '15 at 07:06
  • 1
    I ended up just using pem or p12 format + the "email" identifier. (so I have it working, I just was curious as to why they'd give us the option to download a json that is essentially not readable by their driver). – CasualT Aug 10 '15 at 17:53
  • Here are the exact steps to download the json. Using intuition did not work. https://developers.google.com/sheets/api/quickstart/java#step_1_turn_on_the_api_name – neeraj Mar 28 '18 at 11:51

2 Answers2

4

I had exactly the same problem with the original json secret file I downloaded from Google Cloud.

Everything worked fine by setting a env variable containing the json secret filepath

set GOOGLE_APPLICATION_CREDENTIALS "secret.json path"

and running this line :

GoogleCredential credential = GoogleCredential.getApplicationDefault();

But problems like yours appeared when i wanted to include my secret file as a ressource in my project.

So I finally found that this simple line solved the problem:

GoogleCredential credential =
    GoogleCredential.fromStream(MyClass.class.getResourceAsStream("/client_secrets.json"));

Hope it helps

Armand
  • 251
  • 4
  • 6
2

Actually you are not, I was harassed in a similar manner with Google Drive problems, but this code worked for me. I know you will think that your code is the same, but just try it out.

private static final List<String> SCOPES =
            Arrays.asList(DriveScopes.DRIVE);
     @Override
        public Credential authorize() throws IOException {
            InputStream in =
                    DriveQuickstartImpl.class.getResourceAsStream("/client_secret.json");
            GoogleClientSecrets clientSecrets =
                    GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));

            GoogleAuthorizationCodeFlow flow =
                    new GoogleAuthorizationCodeFlow.Builder(
                            HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                            .setDataStoreFactory(DATA_STORE_FACTORY)
                            .setAccessType("online").setApprovalPrompt("auto")
                            .build();
            Credential credential = new AuthorizationCodeInstalledApp(
                    flow, new LocalServerReceiver()).authorize("user");
            if(credential!=null && credential.getRefreshToken() != null){
                storeCredentials(credential);
            }
           return credential;

        }

If it doesnt work, let me know, I will remove it, my client_secret is in resources folder.

We are Borg
  • 5,117
  • 17
  • 102
  • 225
  • Let me try that inbetween deploy times and I'll get back to you, thanks Mr Borg. – Jan Vladimir Mostert Jul 04 '15 at 06:17
  • Nope, copied your code as is and still getting the same issue. It's failing in the `GoogleClientSecrets` class on this line: `public GoogleClientSecrets.Details getDetails() { Preconditions.checkArgument(this.web == null != (this.installed == null));` I've regenerated the credential file, and still no luck. Keep your code up, will keep on tinkering to see if I can pin down the root cause of the problem. – Jan Vladimir Mostert Jul 06 '15 at 08:48
  • 1
    Ok, Paste your entire POM.xml in main post or pastebin.com. Also, can you post the entire log. Thanks. – We are Borg Jul 06 '15 at 08:53
  • I've updated the question with pom file, source code, stacktrace, as well as code snippets from the decompiled classes. – Jan Vladimir Mostert Jul 06 '15 at 09:49
  • 1
    I hope your JSON starts with Installed rather than WEB... I am going for a break, I might need some time till I am back. – We are Borg Jul 06 '15 at 09:55
  • Included my kots.json file, it doesn't have either installed or web. I've also included a screenshot showing where I got the json file from (I obviously renamed it to kots.json, otherwise it would have been the AppName + a GUID). – Jan Vladimir Mostert Jul 06 '15 at 10:21
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82480/discussion-between-we-are-borg-and-jan-vladimir-mostert). – We are Borg Jul 06 '15 at 10:57