5

I'm trying to access Googles Contacts API but my attempt failed already on getting authorized. From other (web) languages i'm used to the APIConsole and the public API-key (authorization).

GoogleCredential credential = new GoogleCredential().setAccessToken("<<PublicAPIKey>>");
System.out.println(credential.refreshToken());          // false

This way I'm not able to refresh the token and be unsure about using the public-key as accesstoken... Instead I tried over a service account:

private static final String USER_ACCOUNT_EMAIL = "[email protected]";
private static final String SERVICE_ACCOUNT_EMAIL = "[email protected]";
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "xy.p12";

public App() {
    Set<String> scopes = new HashSet<String>();
    scopes.add("https://www.google.com/m8/feeds");

    try {
        GoogleCredential credential = new GoogleCredential.Builder()
            .setTransport(new NetHttpTransport())
            .setJsonFactory(new JacksonFactory())
            .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
            .setServiceAccountScopes(scopes)
            .setServiceAccountUser(USER_ACCOUNT_EMAIL)
            .setServiceAccountPrivateKeyFromP12File(new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
            .build();

        System.out.println(credential.refreshToken());
        //System.out.println(credential.getAccessToken());
    } catch (GeneralSecurityException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

Here my exception:

com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
        at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
        at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
        at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
        at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
        at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
        at App.<init>(App.java:50)
        at App.main(App.java:29)

Thanks for a hint!

4
  • Are you sure that a service account has contacts? Commented Apr 30, 2015 at 12:05
  • Not really but over oauth1 like here isn't an alternative - it closes down in 6 days... and I dont get the mechanism over the public API access. Commented Apr 30, 2015 at 12:16
  • Worst workaround ever: If I made a request to another API e.g Google Plus, the first Request returns a valid access-token. From there on I can access over the setOAuth2Credentials() to the Contacts API. Not amused :) Commented Apr 30, 2015 at 13:48
  • Post that as an answer. I love hacks might be useful someday. Background: contacts API is an old gdata API, very basic and a pain. Google plus is a discovery API and much easer to work with. Commented Apr 30, 2015 at 13:51

1 Answer 1

0

Without calling setServiceAccountUser() my code just worked perfectly fine. But you just will have an impersonated account (service_account_mail) not your personal contacts.

Another possible source for a "401 Unauthorized" exception is leaving the credential.refreshToken() away. The call is necessary to write the access-code into the reference.

Below the finished class:

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.jackson2.JacksonFactory;
import com.google.gdata.client.contacts.ContactsService;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;

public class Connector {

  private static ContactsService contactService = null;
  private static HttpTransport httpTransport;

  private static final String APPLICATION_NAME = "Your-App/1.0";
  private static final String SERVICE_ACCOUNT_EMAIL = "[email protected]";

  private Connector() {
    // explicit private no-args constructor
  }

  public static ContactsService getInstance() {
    if (contactService == null) {
      try {
        contactService = connect();

      } catch (GeneralSecurityException | IOException e) {
        e.printStackTrace();
      }
    }

    return contactService;
  }

  private static ContactsService connect() throws GeneralSecurityException, IOException {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();

    // @formatter:off
    GoogleCredential credential = new GoogleCredential.Builder()
                                            .setTransport(httpTransport)
                                            .setJsonFactory(JacksonFactory.getDefaultInstance())
                                            .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
                                            .setServiceAccountScopes(Collections.singleton("https://www.google.com/m8/feeds"))
                                            .setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
                                            .build();
    // @formatter:on

    if (!credential.refreshToken()) {
      throw new RuntimeException("Failed OAuth to refresh the token");
    }

    ContactsService myService = new ContactsService(APPLICATION_NAME);
    myService.setOAuth2Credentials(credential);
    return myService;
  }

}
Sign up to request clarification or add additional context in comments.

2 Comments

but what if you wanted the personal account instead of the service account?
Hey @StephanCelis Try this as an untested assumption how the api works with personal accounts. Replace the connect function with this: private static ContactsService connect(String uname, String pw) throws GeneralSecurityException, IOException { contactService = new ContactsService(APPLICATION_NAME); try { contactService.setUserCredentials(uname, pw); } catch (AuthenticationException exception) { exception.printStackTrace(); } return myService; } Don't forget to enable less secure apps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.