Gmail API and workload identity federation with AWS and Google Cloud

279 views Asked by At

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: "[email protected]",//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: "[email protected]", //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?

0

There are 0 answers