Not able to watch Admin Users Directory using `google-admin-sdk`
Asked Answered
K

1

0

I am trying to connect to the G-Suite's User directory using the google-admin-sdk. I am using an API Key for authorization and I am not able to reach a successful execution.

Here is the code snippet that I'm using:

import { google } from 'googleapis';
import uuid from 'uuid/v4';

const API_KEY = 'my api key goes here';

google.admin({
  version: 'directory_v1',
  auth: API_KEY
}).users.list({
    customer: 'my_customer',
    maxResults: 10,
    orderBy: 'email',
  }, (err, res: any) => {
    if (err) { return console.error('The API returned an error:', err.message); }

    const users = res.data.users;
    if (users.length) {
      console.log('Users:');
      users.forEach((user: any) => {
        console.log(`${user.primaryEmail} (${user.name.fullName})`);
      });
    } else {
      console.log('No users found.');
    }
  });

Output:

Login Required

Can someone tell me what I am doing wrong here? Also, how do I proceed further for listening to the events emitted by the Google API?

---UPDATE---

Here is the snippet that works for me now:

import { JWT } from 'google-auth-library';
import { google } from 'googleapis';

// Importing the serivce account credentials
import { credentials } from './credentials';

const scopes = ['https://www.googleapis.com/auth/admin.directory.user'];
const adminEmail = 'admin_account_email_address_goes_here';
const myDomain = 'domain_name_goes_here';

async function main () {
  const client = new JWT(
    credentials.client_email,
    undefined,
    credentials.private_key,
    scopes,
    adminEmail
  );
  await client.authorize();
  const service = google.admin('directory_v1');

  const res = await service.users.list({
    domain: myDomain,
    auth: client
  });
  console.log(res);
}

main().catch(console.error);

--- Bonus Tip --- If you face any Parse Errors while using other methods of the directory, remember to JSON.stringify the request body. For example, on the admin.users.watch method:

// Watch Request
const channelID = 'channel_id_goes_here';
const address = 'https://your-domain.goes/here/notifications';
const ttl = 3600; // Or any other TTL that you can think of
const domain = 'https://your-domain.goes';

const body = {
  id: channelID,
  type: 'web_hook',
  address,
  params: {
    ttl,
  },
};

// Remember to put this in an async function
const res = await service.users.watch({
  domain,
  customer: 'my_customer',
  auth: client, // get the auth-client from above
  event: 'add'
}, {
  headers: {
    'Content-Type': 'application/json'
  },
  // This is the important part
  body: JSON.stringify(body),
});
Kosciusko answered 6/2, 2020 at 9:9 Comment(0)
L
2

As you can see in the official documentation, every request sent "to the Directory API must include an authorization token". In order to authorize your request, you have to use OAuth 2.0.

You are providing an API key instead, which is not appropriate for this process. API keys are usually used for accessing public data, not users' private data as in your current situation.

You should follow the steps provided in the Node.js Quickstart instead:

  • First, obtain client credentials from the Google API Console.
  • Second, authorize the client: obtain an access token after setting the user credentials and the appropriate scopes (a process accomplish in functions authorize and getNewToken in the Quickstart).
  • Finally, once the client is authorized, call the API (function listUsers).

Update:

If you want to use a Service Account for this, you will have to follow these steps:

  • Grant domain-wide delegation to the Service Account by following the steps specified here.
  • In the Cloud console, create a private key for the Service Account and download the corresponding JSON file. Copy it to your directory.
  • Use the Service Account to impersonate a user who has access to this resource (an Admin account). This is achieved by indicating the user's email address when creating the JWT auth client, as indicated in the sample below.

The code could be something along the following lines:

const {google} = require('googleapis');
const key = require('./credentials.json'); // The name of the JSON you downloaded

const jwtClient = new google.auth.JWT(
  key.client_email,
  null,
  key.private_key,
  ['https://www.googleapis.com/auth/admin.directory.user'],
  "admin@domain" // Please change this accordingly
);

// Create the Directory service.
const service = google.admin({version: 'directory_v1', auth: jwtClient});

service.users.list({
  customer: 'my_customer',
  maxResults: 10,
  orderBy: 'email',
}, (err, res) => {
  if (err) return console.error('The API returned an error:', err.message);

  const users = res.data.users;
  if (users.length) {
    console.log('Users:');
    users.forEach((user) => {
      console.log(`${user.primaryEmail} (${user.name.fullName})`);
    });
  } else {
    console.log('No users found.');
  }
});

Reference:

I hope this is of any help.

Lenee answered 6/2, 2020 at 10:29 Comment(3)
I have obtained credentials for a service account, which is a robot account and enabled server-to-server communication according to this page: developers.google.com/admin-sdk/directory/v1/guides/delegation But I am not able to figure out how to use it as wellKosciusko
@Kosciusko I updated my answer so that this can be used with a Service Account. Notice that you have to impersonate an admin user in order to access this resource. And please let me know if that works for you.Lenee
I have updated my question to reflect the changes after your answer. Thanks @iamblichusKosciusko

© 2022 - 2024 — McMap. All rights reserved.