Jan Vladimir Mostert
Jan Vladimir Mostert

Reputation: 12972

Connecting to Google Compute Engine using Oauth

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.

Upvotes: 5

Views: 4231

Answers (2)

Armand
Armand

Reputation: 321

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

Upvotes: 4

We are Borg
We are Borg

Reputation: 5311

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.

Upvotes: 2

Related Questions