19

I am embarrassed that I'm simply failing with an example piece of code, but I'll blame it on the fact that it is late...

I have taken a copy and paste of: https://developers.google.com/gmail/api/quickstart/quickstart-java and downloaded the client libraries: https://code.google.com/p/google-api-java-client/ and https://developers.google.com/api-client-library/java/apis/gmail/v1

When I run the sample, I get the following exception:

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 com.emailreply.musterion.GmailApiQuickstart.main(GmailApiQuickstart.java:40)

Googling, I can't find a simple answer, so am assuming stupidity or a library missing/incorrect.

The libraries as I have them are:

/libs/commons-logging-1.1.1.jar
/libs/google-api-client-1.19.0.jar
/libs/google-api-client-android-1.19.0.jar
/libs/google-api-client-appengine-1.19.0.jar
/libs/google-api-client-gson-1.19.0.jar
/libs/google-api-client-jackson2-1.19.0.jar
/libs/google-api-client-java6-1.19.0.jar
/libs/google-api-client-servlet-1.19.0.jar
/libs/google-http-client-1.19.0.jar
/libs/google-http-client-android-1.19.0.jar
/libs/google-http-client-appengine-1.19.0.jar
/libs/google-http-client-gson-1.19.0.jar
/libs/google-http-client-jackson2-1.19.0.jar
/libs/google-http-client-jdo-1.19.0.jar
/libs/google-oauth-client-1.19.0.jar
/libs/google-oauth-client-appengine-1.19.0.jar
/libs/google-oauth-client-java6-1.19.0.jar
/libs/google-oauth-client-jetty-1.19.0.jar
/libs/google-oauth-client-servlet-1.19.0.jar
/libs/gson-2.1.jar
/libs/httpclient-4.0.1.jar
/libs/httpcore-4.0.1.jar
/libs/jackson-core-2.1.3.jar
/libs/jdo2-api-2.3-eb.jar
/libs/jetty-6.1.26.jar
/libs/jetty-util-6.1.26.jar
/libs/jsr305-1.3.9.jar
/libs/transaction-api-1.1.jar
google-api-services-gmail-v1-rev10-1.19.0.jar

The example mentioned above:

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

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.auth.oauth2.GoogleOAuthConstants;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
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.jackson2.JacksonFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.ListThreadsResponse;
import com.google.api.services.gmail.model.Thread;


public class GmailApiQuickstart {

  // Check https://developers.google.ciom/gmail/api/auth/scopes for all available scopes
  private static final String SCOPE = "https://www.googleapis.com/auth/gmail.readonly";
  private static final String APP_NAME = "Gmail API Quickstart";
  // Email address of the user, or "me" can be used to represent the currently authorized user.
  private static final String USER = "me";
  // Path to the client_secret.json file downloaded from the Developer Console
  private static final String CLIENT_SECRET_PATH = "./client_secret.json";


  public static void main (String [] args) throws IOException {
    HttpTransport httpTransport = new NetHttpTransport();
    JsonFactory jsonFactory = new JacksonFactory();

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory,  new BufferedReader(new InputStreamReader(GmailApiQuickstart.class.getResourceAsStream(CLIENT_SECRET_PATH))));

    // Allow user to authorize via url.
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        httpTransport, jsonFactory, clientSecrets, Arrays.asList(SCOPE))
        .setAccessType("online")
        .setApprovalPrompt("auto").build();

    String url = flow.newAuthorizationUrl().setRedirectUri(GoogleOAuthConstants.OOB_REDIRECT_URI).build();


    System.out.println("Please open the following URL in your browser then type the authorization code:\n" + url);

    // Read code entered by user.
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String code = br.readLine();

    // Generate Credential using retrieved code.
    GoogleTokenResponse response = flow.newTokenRequest(code)
        .setRedirectUri(GoogleOAuthConstants.OOB_REDIRECT_URI).execute();
    GoogleCredential credential = new GoogleCredential()
        .setFromTokenResponse(response);

    // Create a new authorized Gmail API client
    Gmail service = new Gmail.Builder(httpTransport, jsonFactory, credential)
        .setApplicationName(APP_NAME).build();

    // Retrieve a page of Threads; max of 100 by default.
    ListThreadsResponse threadsResponse = service.users().threads().list(USER).execute();
    List<Thread> threads = threadsResponse.getThreads();

    // Print ID of each Thread.
    for (Thread thread : threads) {
        System.out.println("Thread ID: " + thread.getId());
    }
  }

}

I replaced the reference to CLIENT_SECRET_PATH with:

new BufferedReader(new InputStreamReader(GmailApiQuickstart.class.getResourceAsStream(CLIENT_SECRET_PATH)))

for no other reason than to try something different. It does work and reads the file correctly.

Any ideas?

Glenn
  • 8,932
  • 2
  • 41
  • 54
Andy
  • 1,421
  • 1
  • 16
  • 22

2 Answers2

54

Right, after some more research (asking a colleague/genius), I found the problem. Basically the GoogleClientSecrets object was not being properly bound with the information from my client_secrets.json file. This meant that during authentication, objects were null resulting in the IllegalArgumentException.

So the original file which looked like this:

{
      "private_key_id": "zzz",
      "private_key": "-----BEGIN PRIVATE KEY-----\nxyz\n-----END PRIVATE KEY-----\n",
      "client_email": "1234@developer.gserviceaccount.com",
      "client_id": "1wdfghyjmp.apps.googleusercontent.com",
      "type": "service_account"
}

was edited to look like this:

{
    "web" : {
      "private_key_id": "zzz",
      "private_key": "-----BEGIN PRIVATE KEY-----\nxyz\n-----END PRIVATE KEY-----\n",
      "client_email": "1234@developer.gserviceaccount.com",
      "client_id": "1wdfghyjmp.apps.googleusercontent.com",
      "type": "service_account"
    }
}

This allowed me to progress through the code with authentication.

Hope this helps.

Glenn
  • 8,932
  • 2
  • 41
  • 54
Andy
  • 1,421
  • 1
  • 16
  • 22
  • 2
    Exxxxtremelyyy helpul... thanks! how did you discovered that? I mean, why putting this "web" solves. – G Bisconcini Jun 26 '15 at 17:08
  • 2
    Similar issue. The original file was generated on Google Console with a "service account". The json file has no "web" element and building the flow balks. Regenerating the credentials using "web" rather than "service account" provides a structure including "web" and the "flow" built. – Glenn Jun 22 '16 at 19:28
  • 2
    Google example just suck – Long Nguyen Dec 21 '17 at 08:05
  • OMG the Google documentation on this stuff is bad enough without needing to restructure the credentials file!! – Chris Halcrow Dec 17 '18 at 06:13
  • Thanks to you and your colleague. Amazing fix to an absurd situation. – seinecle Feb 24 '19 at 20:11
  • I don't think this is the right answer. The Quickstart example does not work with Service accounts out-of-the-box. If you read the guide [Using OAuth 2.0 for Server to Server Applications](https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests) section "Preparing to make an authorized API call", it shows a different way of building `Credentials` object. Basically you need to replace `getCredentials()` code in Quickstart with the code from this guide. – Martynas Jusevičius Apr 01 '19 at 22:31
  • That said, for me the JSON credentials didn't work with my Service account, probably because I've enabled "domain-wide access". I generated a P12 key for the account instead of JSON and that solved the problem. – Martynas Jusevičius Apr 01 '19 at 22:32
5

here is a working example of non-interactive auth for Google Non-interactive authorization with Google OAuth2 the problem is not in "web" tag in the client json, but rather in the fact that their example is for web authentication while their suggested way of generating credentials is for non-interactive "service account". they have multiple problems with their documentation.

Alex
  • 2,589
  • 3
  • 35
  • 44