3

I want to trigger a GCP cloud function from a simple nodejs app running locally.

Reading the documentation it should be simple:

  1. run gcloud auth application-default login to write ADC to file used by client libraries.
  2. use google-auth-library to get a http client to use to trigger the function.
/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// Example: https://my-cloud-run-service.run.app/books/delete/12345
// const url = 'https://TARGET_HOSTNAME/TARGET_URL';

// Example (Cloud Functions): https://project-region-projectid.cloudfunctions.net/myFunction
const targetAudience = 'https://<REGION>-<PROJECTID>.cloudfunctions.net/<FUNCTIONNAME>';
const { GoogleAuth } = require('google-auth-library');

const auth = new GoogleAuth();
const payload = {"prop1": "prop1Value"};

async function request() {
  const client = await auth.getIdTokenClient(targetAudience);
  const resp = await client.request({ url: targetAudience, method: 'POST', data: payload });
  console.info(`Resp status: ${resp.status}; resp.data: ${resp.data}`);
}

(async () => {
  await request();
})();

My understanding was that the google-auth-library would pick up the ADC from the file setup from running gcloud auth application-default login and everything would work. My user has permission to invoke GCP functions as I can trigger the function using CURL with the header -H "Authorization:bearer $(gcloud auth print-identity-token)" \

However when I run this, it doesn't get past the line:

const client = await auth.getIdTokenClient(targetAudience);

Failing with:

Cannot fetch ID token in this environment, use GCE or set the GOOGLE_APPLICATION_CREDENTIALS environment variable t o a service account credentials JSON file.

Using PubSub library works fine so expect ADC does work just not sure what am I missing when trying to trigger the GCP function. Am I using the google-auth-library correctly here ?

Thanks

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
Danny
  • 85
  • 5
  • In your case, you should specify the credentials parameter when calling `GoogleAuth()`. Refer to the source code here: https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/credentials.js – John Hanley Jun 20 '22 at 19:32
  • @JohnHanley, I'm not convinced I should need to provide credentials. I thought these auth libraries would be picking up credentials from the ADC file. This must be what the @google-cloud/pubsub library does. It works locally if I've generated the ADC file using `gcloud auth application-default login` and stops working as soon as I revoke the application-default login. I can't see why the same can't be achieved for calling a GCP function. – Danny Jun 21 '22 at 11:35
  • ADC is not a credential. ADC is a method of searching your environment for existing credentials. You must provide credentials via one of the supported methods. For development use the CLI or the environment variable. For production software provide the credentials in the application or use the metadata server when running on a compute service. – John Hanley Jun 21 '22 at 17:24
  • @JohnHanley, Am I right that ADC searches for credentials first via the environment variable, then by looking for the application_default_credentials.json file which is generated by running `gcloud auth application-default login` The documentation for the above command says NOT to set the environment variable. I have observed when using the PubSub client library, it fails to publish ('Could not load the default credentials') unless I have run `gcloud auth application-default login`. So it appears PubSub library uses my users credentials for authenticating. – Danny Jun 22 '22 at 07:07
  • The nodejs code I mention in the post will ultimately run in Cloud Run env and it works to trigger the GCP function exactly as it is above.. new GoogleAuth(); auth.getIdTokenClient(functionUrl); client.request(..); But I would also like to be able to run and debug this nodejs code locally without having to change the code. What I have read suggested application-default login should have allowed this for me and as it allows PubSub to work. – Danny Jun 22 '22 at 07:17
  • If you are running your code in Cloud Functions, use the credentials from the metadata server. The library will fetch those by default unless specified by other means. To use the same code locally, either set up the environment variable or the CLI credentials via **gcloud auth application-default login**. There are many articles on this topic, I wrote a few of them. – John Hanley Jun 22 '22 at 07:28
  • 1
    The last comment is not a solution. The line `await auth.getIdTokenClient(targetAudience)` _does not_ honour `gcloud auth application-default login` which is a massive limitation and inconsistent with other google lib functions. Seems like a defect needs to be raised. – Matt Byrne Apr 27 '23 at 02:05

2 Answers2

1

As mentioned in the thread:

gcloud auth activate-service-account --key-file is only for "you" running gcloud commands, it won’t be picked up by "applications" that need GOOGLE_APPLICATION_CREDENTIALS. As you can see from Invoke a Google Cloud Run from java or How to call Cloud Run from outside of Cloud Run/GCP?, you either need to have the JSON key file of Service Account, or have to be running inside a GCE/GKE/Cloud Run/App Engine/GCF instance.

For this to work on your local environment, I recommend logging in with gcloud auth application-default login command (this command is meant to work as if you’ve set GOOGLE_APPLICATION_CREDENTIALS locally).

If that doesn't work, as a last resort you can refactor your code to pick up identity token from an environment variable (if set) while working locally,

such as: $ export ID_TOKEN="$(gcloud auth print-identity-token -q)" $ ./your-app

To know more about how the code does it with a JSON key file,refer to the link and similar implementation there. For more information you can refer to a similar thread stated as :

  • Give the default service account access rights to Workspace resource(s) you're attempting to access.

  • Use the JSON key file you set up locally already, to have the Cloud Function run as the same user as is happening when you run locally.

  • Essentially do a hybrid where you create a new service account that ONLY has the permissions you want (instead of using the default
    service account or your personal user, both of which might have far
    more permissions then desired for safety/security), use a key file to run the Cloud Function under that identity, and only give the desired permissions to that service account.

Divyani Yadav
  • 1,030
  • 4
  • 9
1

There are, of course, ways around your issue but fundamentally this library seems to be missing the support of application-default credentials locally - a feature that many other google libs support out of the box. Requiring service account JSON files locally is an older approach.

This appears like a bug or at least missing feature and I've raised an issue in the client lib: https://github.com/googleapis/google-auth-library-nodejs/issues/1543

Local development without this support means that all developers need to export a privileged service account JSON key, store it locally, and configure an environment variable. GCS, secrets manager, etc, etc all just work "out-of-the-box" with locally authenticated application-default credentials if you use gcloud auth application-default login. It would be a shame to force this burden, (and potential security issues with JSON keys floating around the place), on our local developers for one library so hopefully the issue gains traction.

Matt Byrne
  • 4,908
  • 3
  • 35
  • 52
  • Exactly that! Thanks for confirming what I was thinking and raising the issue in Github. – Danny Apr 28 '23 at 07:07