1
votes

(I've reviewed similar questions on SO, however none of the suggestions have worked for me)

I have an API service that runs on Google App Engine (on the Google Cloud Platform). I need to authenticate to the Google Admin Directory API so that I can create groups on our GSuite domain.

I initially tried implicit authorisation by following the guide to Obtaining and providing service account credentials manually, but I think that may be limited to communication between apps that are running on Google App Engine. My understanding is that talking to a Google API outside of App Engine requires OAuth 2.0, so I've set things up as follows:

I've set up the App Engine default service account, and I've granted domain wide delegation to the account from within the Google Cloud Platform. I've also delegated domain-wide authority from the GSuite side, by following this guide - https://developers.google.com/admin-sdk/reports/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account

I've generated a P12 key for the service account. I'm then setting up credentials and using them to instantiate a Google Admin Directory client API as follows:

final List<String> SCOPES = Collections.singletonList(DirectoryScopes.ADMIN_DIRECTORY_GROUP);

final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

/** Email of the Service Account */
final String SERVICE_ACCOUNT_EMAIL = "[email protected]";
final String USER_EMAIL = "[email protected]";

/** Path to the Service Account's Private Key file */
final String SERVICE_ACCOUNT_PKCS12_FILE = "myPath.p12";

String p12Password = "notasecret";

ClassLoader classLoader = this.getClass().getClassLoader();

KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = classLoader.getResourceAsStream(SERVICE_ACCOUNT_PKCS12_FILE);

if (keyFileStream == null){
throw new Exception("Key File Not Found.");
}

keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey pk = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());

GoogleCredential credential = new GoogleCredential.Builder()
    .setTransport(HTTP_TRANSPORT)
    .setJsonFactory(JSON_FACTORY)
    .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
    .setServiceAccountScopes(SCOPES)
    .setServiceAccountUser(USER_EMAIL)
    .setServiceAccountPrivateKey(pk)
    .build();


Directory directory = new Directory.Builder(HTTP_TRANSPORT, JSON_FACTORY, null)
.setHttpRequestInitializer(credential).build();

com.google.api.services.admin.directory.model.Group group = new Group();
group.setEmail("[email protected]");
group.setName("test_group");
group.setDescription("test_group_desc");

Group googleGroup = directory.groups().insert(group).execute();

// error on the line above (403: Not Authorized to access this resource/api - forbidden)

I get an error on the final line, where I try to create a new group:

Group googleGroup = directory.groups().insert(group).execute();
403: Not Authorized to access this resource/api - forbidden

What do I need to do to successfully authenticate?

1

1 Answers

3
votes

Your solution is to follow the documentation.

  1. Do not use an App Engine Service Account. Create a new Service Account.
  2. Do not use P12 (PFX) as they are deprecated. Most APIs no longer support them. Use JSON credential file.
  3. You need to delegate Domain-Wide Authority to the Service Account.
  4. Using the Service Account credentials you impersonate another User that has domain superadmin privilege and who has logged in at least once into G Suite and accepted the terms and conditions.

Perform G Suite Domain-Wide Delegation of Authority