I'm trying to invoke a service which I have deployed to Google Cloud Run. It is a simple 'hello world' service but with 'require authentication' as the authentication option.
I am trying to call it from an AWS EC2 instance, using the workflow identity federation. I have followed the instructions here https://cloud.google.com/iam/docs/workload-identity-federation-with-other-clouds (and I also found this helpful) and I have proved that it is successfully impersonating the service account, as this following code running from the EC2 instance works to successfully list the GCP storage buckets:
const {GoogleAuth} = require('google-auth-library');
async function main() {
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform'
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
console.log('projectId = ', projectId)
// List all buckets in a project.
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
const res = await client.request({ url });
console.log(res.data);
}
main().catch(console.error);
However, when I try to call my Cloud Run service using the following code:
const {GoogleAuth} = require('google-auth-library');
async function main() {
const targetAudience = 'https://my-cloud-run-service.a.run.app/'
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
targetAudience: targetAudience
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
console.log('projectId = ', projectId)
// it's fine up to here
const url = targetAudience;
// this fails with HTTP error 401 unauthorised
const response = await client.request({url});
console.log(response);
}
main().catch(console.error);
then it fails with:
GaxiosError:
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL <code>/</code>.</h2>
<h2></h2>
</body></html>
at Gaxios._request (/home/ubuntu/trader2/node_modules/gaxios/build/src/gaxios.js:141:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async AwsClient.requestAsync (/home/ubuntu/trader2/node_modules/google-auth-library/build/src/auth/baseexternalclient.js:246:24)
at async main (/home/ubuntu/trader2/testAuth.js:15:22)
Now if I set GOOGLE_APPLICATION_CREDENTIALS to the service account key itself, rather than the file downloaded when I grant the service account access to the workflow identity federation, and call getIdTokenClient instead of getClient, then it works fine.
This is kind of acceptable, because as long as I protect the service account key file, then it's still secure enough. But I would just like to get the recommended way working if possible - frustrating that there must be one piece of the puzzle I'm missing!
Does anyone know a configuration step I may have missed, anything to test, or if there's anything wrong with my code?
Any help much appreciated
Success - I've got it working using the below code.
Many thanks @JohnHanley - this info you provided me about using the access token to exchange for an OIDC access token, the difference between the authorization requirements between google services and invoking user-managed cloud run services, and the endpoint to use to gain the OIDC token, has just provided the missing piece of the puzzle. So thanks again, much appreciated.
However I do think like @guillaume-blaquiere says, it would be nice if this were part of the library.