googleapis / google-auth-library-nodejs

🔑 Google Auth Library for Node.js
Apache License 2.0
1.71k stars 376 forks source link

Support ID Token Fetching With User Credentials #1551

Closed tilgovi closed 1 year ago

tilgovi commented 1 year ago

Google auth libraries for other languages support getting identity tokens for an arbitrary audience with user credentials but this one does not. In addition to the language SDKs, gcloud CLI supports print-identity-token --audiences=....

I'm filing this ticket as a feature request to clarify what I think is the underlying issue behind #1543 and the (closed but maybe shouldn't be) #1113.

The following should working after setting up application default credentials on a developer machine:

import { auth } from 'google-auth-library';
await auth.getIdTokenClient('my-audience');

Perhaps it would be best if auth.getRequestHeaders('audience') also used the ID token client when the audience is not a Google API supporting Google access tokens, as is done with other credential types.

Lack of support for this provides friction for local development of any application that calls services using Identity-Aware Proxy or Cloud Endpoints with Google ID Token authentication.

tilgovi commented 1 year ago

I may actually be wrong here, but want to consolidate a focused conversation on the subject.

Java library supports this, but it requires a cast:

((IdTokenProvider)GoogleCredentials.getApplicationDefault()).idTokenWithAudience("audience", new ArrayList<IdTokenProvider.Option>())

Python library seems not to support this.

What's the intention? How are users of Google auth libraries meant to call services using IAP or Cloud Endpoints during local development without long-lived service account keys?

tilgovi commented 1 year ago

The correct way to do this might be to use service account impersonation. For calling endpoints that use Cloud Endpoints or IAP, that might mean being explicit about the use of an ID token client, rather than assuming that the default credentials client is a self-signed JWT client.

Do:

const client = await auth.getIdTokenClient(audience);
const headers = await client.getRequestHeaders(url);

Don't:

const headers = await auth.getRequestHeaders(audience);
keithw99 commented 1 year ago

Python library seems not to support this.

Python actually does support this:


import requests
import google.auth.transport.requests as google_requests
import google.oauth2.id_token

AUDIENCE = 'https://my.cloud.run.app' auth_req = google_requests.Request() id_token = google.oauth2.id_token.fetch_id_token(auth_req, AUDIENCE) headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {id_token}', }

Here is the actual request using the fetched token.

response = requests.post( 'https://my.cloud.run.app/my-endpoint', json={ 'a_field_name': 'some data' }, headers=headers, )

This works 100% with user credentials downloaded from `gcloud auth application-default login`. No service account key needed, and no `GOOGLE_APPLICATION_CREDENTIALS` needed. However, a NodeJS client trying to do the exact same thing will fail with this exception:

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


It seems that the NodeJS auth library is simply not at parity with the other Google auth libraries, and I agree that https://github.com/googleapis/google-auth-library-nodejs/issues/1113 should not be closed. At minimum, the NodeJS client library should clearly document that it does not support user credentials when developing locally.
tilgovi commented 1 year ago

When I do that on my local machine I get a similar error to the Node.js library:

google.auth.exceptions.DefaultCredentialsError: Neither metadata server or valid service account credentials are found.
keithw99 commented 1 year ago

When I do that on my local machine I get a similar error to the Node.js library:

google.auth.exceptions.DefaultCredentialsError: Neither metadata server or valid service account credentials are found.

Hmm, not sure what causes that. I updated my example with more context to illustrate how I used the fetched token in the request. I assume you are doing something similar?

salrashid123 commented 1 year ago

if you're using your user creds and gcloud cli, audience for the id_token you get is static and bound to the client_id for gcloud itself (i.,e "32555940559.apps.googleusercontent.com" for gcloud auth print-identity_token

you simply cant change the audience value for a user-based cred. You can change the audiece value if you use a service account (via a key, via impersonation, using workload federation or gce metadata servers)


the only reason you can call cloud run or cloud functions using your id_token derived from user credentials is because those service (and those services alone, made a weird exemption just for the client_id for gcloud cli and gcloud with ADC login)...which is a hack (the aud: value should match the service its intended for; gcloud's client id is not an intended target for cloud run...)

while google-auth-java does allow deriving id_token from a user's creds...it shoudn't really allow that in the first place (but that ship has sailed)


anyway, if what you want to do is to get id_tokens for other services, see

danielbankhead commented 1 year ago

This is a duplicate of https://github.com/googleapis/google-auth-library-nodejs/issues/876, let's continue the conversation there