sakib11
sakib11

Reputation: 536

Google-api-nodejs-client: Using service account to call directory api returns invalid inputs

I have created a service account and enabled domain-wide delegations to it. I have enabled the admin sdk directory api. I was following this basic example of listing all the users. Up on creating the service account, I downloaded a file. My understanding from this documentation is that I need to pass the credential file to the auth client. But the official example has no description on how to actually pass the file to the authClient. I tried to create a request like this:

    const credentials = await JSON.parse(fs.readFileSync("cred.json"));
    const auth = new google.auth.GoogleAuth({
        credentials: credentials,
        scopes: ["https://www.googleapis.com/auth/admin.directory.user"],
    });

    const authClient = await auth.getClient();
    const { data } = await google
        .admin({ version: "directory_v1", auth: authClient })
        .users.list({
            customer: "my_customer",
        });

But this request returns Invalid Inputs. I can't seem to find any examples or explanations on this topic. It also states that I must impersonate a user. But the docs does not show where to put the user email? Can anyone share their experience of how to deal with this bit? Any sample code, documentation or an explanation would be much appriciated.

Upvotes: 0

Views: 1004

Answers (1)

Giggiux
Giggiux

Reputation: 125

Reading the last example you linked, it looks like you're not authenticating correctly:

// Load client secrets from a local file.
const credentials = await JSON.parse(fs.readFileSync("cred.json"));

// Get authorization using your credentials.
authorize(credentials, listUsers);


/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  const oauth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

  return getNewToken(oauth2Client, callback);
}

/**
 * Get new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */
function getNewToken(oauth2Client, callback) {
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oauth2Client.getToken(code, (err, token) => {
      if (err) return console.error('Error retrieving access token', err);
      oauth2Client.credentials = token;
      callback(oauth2Client);
    });
  });
}


/**
 * Lists users in the domain.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
async function listUsers(auth) {
  const service = google.admin({version: 'directory_v1', auth});
  const {data} = await service.users.list({
    customer: 'my_customer'
    });
}

As you can see in the function getNewToken you actually login via browser with a user.

I simplified the code from the given page of the official example. With this code you will need to login via browser every time you start the application, but if you check the official example, there is a way to store the token received via OAuth2, so that it will not ask always for authentication.

Upvotes: 1

Related Questions