googleapis / google-auth-library-python

Google Auth Python Library
https://googleapis.dev/python/google-auth/latest/
Apache License 2.0
773 stars 305 forks source link

Generate id_token from default credentials #590

Open dinvlad opened 4 years ago

dinvlad commented 4 years ago

Is your feature request related to a problem? Please describe.

Currently, google.auth.default() generates Credentials that contain .token that is an access token. We'd like to also generate id_token with the appropriate audience starting from default credentials.

This would allow us to submit requests that use id_token uniformly among different environments:

It would be great to add a method to google.auth.credentials.Credentials that allows id_token generation, e.g.

credentials.id_token(audience='https://example.org')

Describe alternatives you've considered So far, we had to rely on piece-meal approaches, like this example from https://github.com/apache/airflow/blob/master/airflow/providers/google/common/utils/id_token_credentials.py

This unnecessarily increases the complexity of third-party apps, and we have to re-implement the same logic in each one of them.

It would be preferable to incorporate such logic into this library instead.

Additional context

AndreaGiardini commented 2 years ago

@arithmetic1728 I stumbled upon this issue recently. Any plan to add support for user credentials? Or pointer in the right direction?

romanwozniak commented 2 years ago

This is already 2 years old, but it is still not clear why it's impossible to generate id_token for user credentials. The workaround (i.e. id_token_credentials.py) allows to get the id_token, but not for the right audience. I understand that this is not the limitation of the google-auth library itself, but rather something, that is not supported by the Google Auth API, but still, I don't understand what is the root cause for this.

In my use case, I would want to build an API server (exposed via Cloud Endpoints with OIDC authentication) and a CLI tool, that interacts with this server. Currently, I can't simply use google.auth.default() in the CLI, because this wouldn't give me the ID Token with expected audience.

Can someone give me some leads to what am I doing wrong? Thank you!

NicolaSpreafico commented 6 days ago

No sure if still useful to someone, but after few searches I did not find an actual solution, but instead a clear indication in the documentation that the given method does NOT work for user credentials, and gcloud need to be used

https://cloud.google.com/run/docs/authenticating/service-to-service#use_the_authentication_libraries

This code does not work for obtaining authentication credentials for a user account.

https://cloud.google.com/docs/authentication#user-accounts

Use your user credentials to sign in to the Google Cloud CLI, and then use the tool to generate access tokens.

I ended up with a code like this:

import subprocess

from google.oauth2 import id_token
from google.auth import transport

# FIXME Defines how your code understand if it's running from your local machine or on the GCP infrastructure
is_local_enviroment = True 

if is_local_enviroment is True:

    # FIXME you can generate an idToken for your own user or on behalf of a service account you may want to impersonate
    # for the second case you will need to have role "Service Account Token Creator" on given account
    impersonate_identity = "todo@todo.iam.gserviceaccount.com"

    if impersonate_identity is None:
        command = ['gcloud', 'auth', 'print-identity-token', '--include-email']
    else:
        command = ['gcloud', f'--impersonate-service-account={impersonate_identity}', 'auth', 'print-identity-token', '--include-email']

    try:
        output = subprocess.check_output(command, text=True)
    except subprocess.CalledProcessError as e:
        raise ValueError(f"Command failed with return code {e.returncode}: {str(e)}")

    # strip() is mandatory because the subprocess produces an output with a \n at the end
    generated_id_token = output.strip()

else:

    generated_id_token = id_token.fetch_id_token(request=transport.requests.Request(), audience=''.join(self.audience))

# TODO do something with the id_token...
print(generated_id_token)

Hope this may be useful to someone.

I also find another post on the same topic: https://stackoverflow.com/questions/67113855/how-can-i-retrieve-an-id-token-to-access-a-google-cloud-function/67122583#67122583