RTF
RTF

Reputation: 6524

Google Gmail API request with service credential returning 400 using Java SDK

I'm trying to access my own personal Gmail account using the Gmail API with service account credentials and the Java SDK (v1.22.0). I've generated the credentials and saved the json file locally, then tried running this:

public class GmailAPITest {

public static void main(String[] args) throws Exception {
    JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    HttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

    String filepath = "/path/to/my/creds.json";

    GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(filepath))
            .createScoped(Collections.singleton(GmailScopes.GMAIL_READONLY));

    Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
            .setApplicationName("MyApp")
            .build();

    String user = "me";
    ListLabelsResponse listResponse = service.users().labels().list(user).execute();
    List<Label> labels = listResponse.getLabels();

    if (labels.size() == 0) {
        System.out.println("No labels found.");
    } else {
        System.out.println("Labels:");
        for (Label label : labels) {
            System.out.printf("- %s\n", label.getName());
        }
    }   
}

I get a 400 response like this:

{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Bad Request",
    "reason" : "failedPrecondition"
  } ],
  "message" : "Bad Request"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at ie.test.GmailAPITest.main(GmailAPITest.java:34)

I've tried enabling Domain-wide Delegation on the service credentials, and I've tried changing the user "me" to the email address of my account, but it's the same result. I've also looked at a number of questions with similar problems but I haven't found any of them useful. Am I missing something?

The Gmail API is enabled from the developer console and from there I can see all my failed 4xx requests.

I should also mention that I've created another test using Python, which does exactly the same thing using the same service credentials, and it fails with the same 400 error. I've tried creating a new key (json) under the same security credential and tried using that, but it's the same result.


I've been able to get things working, but only by using the alternative oAuth sign-in process i.e. being prompted to allow/deny access from a web browser, which then saves some oAuth access tokens to a file for future user. This is not ideal, because I'm going to have to constantly renew these tokens. The service credential was presumably created to avoid that exact problem. I'm still very interested in finding out why it's not working for me.

Upvotes: 1

Views: 586

Answers (1)

Jay Lee
Jay Lee

Reputation: 13528

Service account domain wide delegation is for G Suite accounts only and requires the G Suite admin to authorize the service account client id to have access to the necessary scopes. If you are working with consumer Gmail accounts (@gmail.com address) or you don't have G Suite admin approval then you should use normal OAuth flow like you have working now.

Upvotes: 2

Related Questions