401 unauthorized response when calling a Cloud Function using google-auth-library

I’m calling a private http Cloud Function using the google-auth-library in Node.js. This works locally both when authenticated as myself and when impersonating the Cloud Run service account. However, when I deploy this to Cloud Run I get 401 responses on all of these calls.

I cannot figure out why this is. The Cloud Run instance uses the same service account that I had impersonated locally so permissions should not be an issue. Any ideas on what the problem is?

For some extra information, the Cloud Run instance is using a Serverless VPC Access connector for all outbound traffic. I’ve read that this could be a potential cause, but none of the solutions I’ve tried surrounding this have helped.

Below is the basic structure of my code.

import { GoogleAuth } from 'google-auth-library';

async function callCloudFunction() {
  const functionUrl = 'https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME';
  const auth = new GoogleAuth();

  const client = await auth.getIdTokenClient(functionUrl);
  const token = await client.getRequestHeaders();

  const res = await fetch(functionUrl, {
      method: 'GET',
      headers: token
  });

  const data = await res.json();
  return data
}

Hi @mattkocak ,

The 401 Unauthorized issue is likely due to an authentication mismatch or a network routing problem caused by the Serverless VPC Access connector.

The getIdTokenClient method automatically assigns the Cloud Function URL as the audience, but if there’s even a slight mismatch, authentication will fail. Make sure the URL you pass to getIdTokenClient is an exact match to your Cloud Function’s endpoint, including region and project ID. You may also check some sample codes in this repository.

Even though the service account worked locally, Cloud Run may not be passing its identity correctly. Ensure the Cloud Run service account has the roles/run.invoker permission on the Cloud Function.

You may also try these troubleshooting steps provided in this guide:

  • Make sure that your requests include an Authorization: Bearer ID_TOKEN header, and that the token is an ID token, not an access or refresh token. If you generate this token manually with a service account’s private key, you must exchange the self-signed JWT token for a Google-signed Identity token. For more information, see Authenticate for invocation. Invoke your HTTP function using authentication credentials in your request header.

  • Redeploy your function to allow unauthenticated invocations if this is supported by your organization. This is useful for testing.

Was this helpful? If so, please accept this answer as “Solution”. If you need additional assistance, reply here within 2 business days and I’ll be happy to help.

1 Like

Thanks for the response. It turns out the issue was that I was including the extended route/query parameters for my specific request call when calling getIdTokenClient. So I was passing getIdTokenClient more information than required, which was causing the authentication to fail.

I’m not sure why it was working locally.. Anyway, thanks again!

1 Like