4

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 = "xy@gmail.com";
private static final String SERVICE_ACCOUNT_EMAIL = "xy@developer.gserviceaccount.com";
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!

Stephan Celis
  • 2,492
  • 5
  • 23
  • 38
binzram
  • 530
  • 4
  • 13
  • Are you sure that a service account has contacts? – Linda Lawton - DaImTo Apr 30 '15 at 12:05
  • Not really but [over oauth1 like here](https://stackoverflow.com/questions/14407415/accessing-google-contacts-api-via-oauth-2-0-and-private-key-aka-service-account) isn't an alternative - it closes down in 6 days... and I dont get the mechanism over the public API access. – binzram Apr 30 '15 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 :) – binzram Apr 30 '15 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. – Linda Lawton - DaImTo Apr 30 '15 at 13:51

1 Answers1

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 = "xy@developer.gserviceaccount.com";

  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;
  }

}
binzram
  • 530
  • 4
  • 13
  • but what if you wanted the personal account instead of the service account? – Stephan Celis Oct 12 '15 at 09:21
  • 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](https://support.google.com/accounts/answer/6010255?hl=en). – binzram Oct 13 '15 at 11:59