googleapis / google-auth-library-python

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

Please implement with_subject on return type of google.auth.default #1512

Closed adamcunnington-mlg closed 2 months ago

adamcunnington-mlg commented 3 months ago

It's extremely common / sensible to use ADC to write inter-operable code. Occasionally, domain-wide delegation is necessary (e.g. when working with Google Workspace / Cloud Identity APIs).

The credential object returned by google.auth.default() does not have a with_subject method which means you can't easily create delegated creds.

In fact, the only working workaround that I've seen is a sure sign that this is bad design. It is pretty low level and involves using the SA from the ADC to sign and generate new creds. https://stackoverflow.com/questions/53202767/gae-attributeerror-credentials-object-has-no-attribute-with-subject/57092533#57092533

ADC is a method of obtaining credentials, it's not a credential type. If ADC gets me SA cred, I should be able to use with_subject. If not, that method should throw an exception. It's daft the method doesn't exist.

At the moment, I'm doing this code which folk will frown at but it's hard to argue when it is a pragmatic solution for an otherwise daft workaround!

credentials, _ = google.auth.default()
credentials._subject = "foo.bar@baz"

Please advise! Thanks

arithmetic1728 commented 2 months ago

How about the following workaround?

from google.oauth2 import service_account

credentials, _ = google.auth.default()

if isinstance(credentials, service_account.Credentials):
    credentials = credentials.with_subject('subject')
adamcunnington-mlg commented 2 months ago

I don't think the works? I don't think Google Auth default ever returns that object?

I tried setting export GOOGLE_APPLICATION_CREDENTIALS=path/to/keyfile.json and re ran and there's no method on the object. You always get a plain Credentials object

arithmetic1728 commented 2 months ago

It worked for me. I export GOOGLE_APPLICATION_CREDENTIALS to the json file path, and run the following code:

import google.auth
from google.oauth2 import service_account

credentials, _ = google.auth.default()

print(type(credentials))

if isinstance(credentials, service_account.Credentials):
    credentials = credentials.with_subject('subject_foo')
    print(credentials._subject)

here is the output:

<class 'google.oauth2.service_account.Credentials'>
subject_foo
arithmetic1728 commented 2 months ago

google.auth.default creates the credential based on the JSON file type field, for service account JSON file, it creates a service account credentials object, see https://github.com/googleapis/google-auth-library-python/blob/main/google/auth/_default.py#L188 and https://github.com/googleapis/google-auth-library-python/blob/main/google/auth/_default.py#L449

adamcunnington-mlg commented 2 months ago

Hmmm, this is what I was expecting all along and I wouldn't have raised this ticket if I thought this was the case because the behaviour you're sharing matches my expectation. I can't reproduce the issue now but I feel like there is a subtlety here. I'll follow up if I can identify it. Otherwise, thank you.

arithmetic1728 commented 2 months ago

No problem, please reopen the issue once you get more info on the original issue so we can investigate further. I will close the issue for now, thanks!