google-oauthgoogle-admin-sdkgoogle-directory-apigoogle-shared-contactsgoogle-profiles-api

Is it possible to get all users from the same Google Apps domain?


I need to implement the following scenario:

  1. A user signs in with his or her Google credentials.
  2. Thereafter my application checks whether or not this user is using Google Apps.
  3. If he or she is, my application reads all active users in the same Google Apps domain (com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload#getHostedDomain).

It is similar to "Find your friends" feature on Facebook. In this case I want to find all people, who use the same Google Apps domain.

Is it possible to do (both technically and with respect to their terms of use)?

Update 1 (28.08.2017 20:00 MSK):

I've tried to get a list of all users using an access token (gIdToken.payload.accessTokenHash in the code fragment below):

internal open fun createJsonFactory(): JsonFactory = JacksonFactory.getDefaultInstance()

internal open fun createTransport(): HttpTransport = GoogleNetHttpTransport.newTrustedTransport()

internal open fun createVerifier(httpTransport: HttpTransport, jsonFactory: JsonFactory): GoogleIdTokenVerifier {
    return GoogleIdTokenVerifier.Builder(httpTransport, jsonFactory)
            .setAudience(listOf(clientId))
            .build()
}


val idToken = req.queryParams("idtoken")
val httpTransport = createTransport()
val jsonFactory = createJsonFactory()
val verifier = createVerifier(httpTransport, jsonFactory)
val gIdToken = verifier.verify(idToken)
if ((gIdToken != null) && (gIdToken.payload != null)) {
    val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
    val directory = Directory.Builder(httpTransport, jsonFactory, credential)
                      .setApplicationName("Google Auth Test")
                      .build()
    try {
        directory.users().list().setCustomer(gIdToken.payload.subject).execute()
    }
    catch (ex:Throwable) {
        ex.printStackTrace()
    }
}

When I run directory.users().list().setCustomer(gIdToken.payload.subject).execute(), I get the following error:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
  "code" : 401,
  "errors" : [ {
    "domain" : "global",
    "location" : "Authorization",
    "locationType" : "header",
    "message" : "Invalid Credentials",
    "reason" : "authError"
  } ],
  "message" : "Invalid Credentials"
}
  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)

I'm using following Google libraries:

<dependency>
    <groupId>google-api-services</groupId>
    <artifactId>admin-directory_v1</artifactId>
    <version>1.22.0</version>
</dependency>
<dependency>
    <groupId>com.google.gdata</groupId>
    <artifactId>core</artifactId>
    <version>1.47.1</version>
</dependency>

How can I fix the error above?

Update 2 (29.08.2017 11:39 MSK): Tried to use Contacts API

Attempt 1: Contacts API feed

import com.google.gdata.data.extensions.ContactFeed

fun readUsers2(gIdToken:GoogleIdToken, httpTransport: HttpTransport, jsonFactory:JsonFactory) {
    val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
    val service = ContactsService("Google Auth Test")
    service.setOAuth2Credentials(credential)
    val feedUrl = URL("https://www.googleapis.com/auth/contacts.readonly")
    val resultFeed: ContactFeed = service.getFeed(feedUrl, ContactFeed::class.java)
}

Result: Exception "Unrecognized content type: text/plain" in com.google.gdata.client.Service#parseResponseData(com.google.gdata.data.ParseSource, com.google.gdata.wireformats.input.InputProperties, java.lang.Class<E>), see throw statement below.

private <E> E parseResponseData(ParseSource source, InputProperties inputProperties, Class<E> resultType) throws IOException, ServiceException {
    Preconditions.checkNotNull("resultType", resultType);
    AltFormat inputFormat = null;
    String alt = inputProperties.getQueryParameter("alt");
    if (alt != null) {
        inputFormat = this.altRegistry.lookupName(alt);
    }

    if (inputFormat == null) {
        inputFormat = this.altRegistry.lookupType(inputProperties.getContentType());
        if (inputFormat == null) {
            throw new ParseException("Unrecognized content type:" + inputProperties.getContentType());
        }
    }

Attempt 2: Contacts API query

fun readUsers3(gIdToken:GoogleIdToken, httpTransport: HttpTransport, jsonFactory:JsonFactory) {
    val credential = GoogleCredential().setAccessToken(gIdToken.payload.accessTokenHash)
    val service = ContactsService("Google Auth Test")
    service.setOAuth2Credentials(credential)
    val feedUrl = URL("https://www.googleapis.com/auth/contacts.readonly")
    val query = Query(feedUrl)
    val resultFeed = service.query(query, ContactFeed::class.java)
}

Result: Same exception as in attempt 2.

Update 3 (29.08.2017 13:04 MSK):

I tried to get a list of users using the API Explorer.

Screenshot 1

I got the 403 error. Details are given below.

Screenshot 2

Request:

GET https://www.googleapis.com/admin/directory/v1/users?domain={MYDOMAIN}&fields=users(emails%2Cid%2Cname)&key={YOUR_API_KEY}

Response:

403

- Show headers -

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "forbidden",
    "message": "Not Authorized to access this resource/api"
   }
  ],
  "code": 403,
  "message": "Not Authorized to access this resource/api"
 }
}

Update 4 (30.08.2017 13:19 MSK):

Finally, I managed to get rid of authentication and authorization errors (in a test installation of a G suite app).

However, now the following calls return an empty list of users (without errors).

val directory = Directory.Builder(
        NetHttpTransport(),
        JacksonFactory.getDefaultInstance(),
        credential)
        .setApplicationName("Google Auth Test")
        .build()
val users = directory.users().list()

Solution

  • You need to get sufficient permissions from the user to fetch the list of all users in the domain. Moreover, it will only work if logged in user is super admin of the domain.

    You can get list of permissions here.