whiterook6
whiterook6

Reputation: 3544

Authorizing with Google APIs using a service account

I'm trying to access some google groups from the google-api using nodeJS and a service account file. I can't find a code sample that works quite like this. I've put together some code that seems to be working, but I keep getting an error 400 - Bad Request from the API without any clues what's wrong.

Am I using the right classes? Can I use a service account here? Am I missing scopes or something?

My code:

import { Auth, google } from "googleapis";

const main = async () => {
    const auth = new Auth.GoogleAuth({
        keyFile: "/path-to-service-account-file.json",
        scopes: "https://www.googleapis.com/auth/admin.directory.group.readonly",
    });
    const client = await auth.getClient();

    // Obtain a new drive client, making sure you pass along the auth client
    const admin = google.admin({ version: 'directory_v1', auth: client });

    const groups = await admin.groups.list();
    console.log(groups.data.groups);
}

main().then(() => process.exit(0)).catch(err => {
    console.error(err);
    process.exit(1);
});

The file is (I think) a standard service account file:

{
  "type": "service_account",
  "project_id": "groupmembers-*******",
  "private_key_id": "*********",
  "private_key": ...,
  "client_email": "test-admin-nodejs@groupmembers-****.iam.gserviceaccount.com",
  "client_id": "*****",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-admin-nodejs%40groupmembers-******.iam.gserviceaccount.com"
}

When I run this code, I get the following response:

    status: 400,
    statusText: 'Bad Request',
    request: {
      responseURL: 'https://admin.googleapis.com/admin/directory/v1/groups'
    }

What's interesting to me is the bearer token it generates for me. The error also prints out the request info:

config: {
    url: 'https://admin.googleapis.com/admin/directory/v1/groups',
    method: 'GET',
    userAgentDirectives: [ [Object] ],
    paramsSerializer: [Function (anonymous)],
    headers: {
      'x-goog-api-client': 'gdcl/5.1.0 gl-node/17.4.0 auth/7.14.1',
      'Accept-Encoding': 'gzip',
      'User-Agent': 'google-api-nodejs-client/5.1.0 (gzip)',
      Authorization: 'Bearer ya29.c.b0AX***********************************************jzxc........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................',
      Accept: 'application/json'
    },

I don't know if this is normal or relevant, but I've never seen a bearer token with a bunch of dots at the end.

Upvotes: 1

Views: 8128

Answers (1)

Morfinismo
Morfinismo

Reputation: 5253

A 400 error is not an authorization/authentication issue, but instead is a client request issue. According to the official documentation, there are some query parameters required for the api call. One of them is the customer query parameter:

The unique ID for the customer's Google Workspace account. In case of a multi-domain account, to fetch all groups for a customer, fill this field instead of domain. As an account administrator, you can also use the my_customer alias to represent your account's customerId. The customerId is also returned as part of the Users

Based on experience, it is implied you must use this value. Although the documentation should be better and it should explicitly say this is required. So, in summary, your request should look like this:

const groups = await admin.groups.list({
    customer: "my_customer"
});
console.log(groups.data.groups);

Upvotes: 1

Related Questions