skip to Main Content

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()

3

Answers


  1. Check this documentation to retrieve all users in the same domain, use the following GET request and include the authorization described in Authorize requests. For readability, this example uses line returns:

    GET https://www.googleapis.com/admin/directory/v1/users
    ?domain=primary domain name&pageToken=token for next results page
    &maxResults=max number of results per page
    &orderBy=email, givenName, or familyName
    &sortOrder=ascending or descending
    &query=email, givenName, or familyName:the query's value*
    

    For the request and response properties, you may want to use the Users: list method wherein it retrieves a paginated list of either deleted users or all users in a domain. This request requires authorization with at least one of the following scopes (read more about authentication and authorization).

    • https://www.googleapis.com/auth/admin.directory.user.readonly
    • https://www.googleapis.com/auth/admin.directory.user
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. This question is related to Google API/get directory contacts

    I’m posting an answer here since this one is more google friendly:

    The Admin Directory Users:list resource allows company directory listing even without admin privileges.

    Just make sure you set the viewType to domain_public and you’re good to go:

    https://www.googleapis.com/admin/directory/v1/users?customer=my_customer&viewType=domain_public

    A scope of https://www.googleapis.com/auth/admin.directory.user.readonly should be enough.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search