4

I'm looking to integrated with the Gmail API and want to use a service user with domain-wide delegation to request data on behalf of users in Google Workspaces. My code is running in EC2 in AWS. Reading through the docs it seems best practice is to use workload identity federation to authenticate as a service user so there's no risk of the key being leaked.

I've followed the steps on Google's site to setup workload identity federation with AWS and have, in addition, run the following command in the google cloud shell:

gcloud iam service-accounts add-iam-policy-binding <service_account_email>  \    
--role=roles/iam.workloadIdentityUser  \ 
--member="principalSet://iam.googleapis.com/projects/<project_id>/locations/global/workloadIdentityPools/<pool_id>/attribute.aws_role/arn:aws:sts::<aws_account_id>:assumed-role/<aws_role_name>" \
--project <google_cloud_project_name>

I have the following test application code:

const { google } = require("googleapis");
const { GoogleAuth } = require("google-auth-library");
const path = require("path");

module.exports = async (req, res) => {
  const scopes = [
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/admin.directory.user.readonly",
  ];

  let status = "";

  try {

    //Inject the client library config as the GOOGLE_APPLICATION_CREDENTIALS
    const configPath = path.join(__dirname, "client_library_config.json");
    process.env["GOOGLE_APPLICATION_CREDENTIALS"] = configPath;
    process.env["GOOGLE_CLOUD_PROJECT"] = "XXXX";

    const auth = new GoogleAuth({
      scopes,
      subject: "XXXX@XXXX.com",//email address of the test account we want to impersonate
      projectId: "XXXXX", //Google Cloud Project Name
    });

    const gmailClient = google.gmail({ version: "v1", auth });

    const user = await gmailClient.users.threads.list({
      userId: "XXXX@XXXX.com", //email address of the test account whose gmail threads we want to get
    });
  } catch (err) {
    console.log("error", err);
  }
  res.status(200).json({
    status: "ok",
  });
};

When trying to run this code I get the following error message:

error GaxiosError: Precondition check failed.
at Gaxios._request (/opt/backend/node_modules/gaxios/build/src/gaxios.js:130:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async AwsClient.requestAsync (/opt/backend/node_modules/google-auth-library/build/src/auth/baseexternalclient.js:240:24)

What's strange is when I run this code locally with a private key and JWT authentication for the service user it works perfectly and I don't run into any issues. Any ideas what might be going on?

user3766476
  • 155
  • 1
  • 6
  • A failed precondition error message means that some part of the action you are trying to take is requesting access to a resource that is unavailable. I checked through your error response and it seems there is an undefined object being passed to the request string. These undefined objects are listed as [object] in the response. Please check through your request string once more for any resources that you may not have access to. – Jeffrey D. Dec 30 '22 at 23:06
  • @JeffreyD. thanks, I'm not sure I fully understand, which response could we see the list of undefined object/resources we don't have permission to access? Wouldn't the fact that we can make the request with a private key and JWT authentication suggest we have the correct access? – user3766476 Jan 02 '23 at 16:36

0 Answers0