Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default credentials on App Engine standard (Py3) do not support custom scopes #579

Closed
davidwtbuxton opened this issue Aug 5, 2020 · 17 comments
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@davidwtbuxton
Copy link
Contributor

Using the google.auth.default() helper to access credentials for the default service account on App Engine standard works great, but does not allow one to use auth scopes beyond the default scopes for the Python 3.7 and 3.8 runtimes. This is different to how the old 2.7 runtime works.

This app works on 2.7, fails with an exception on 3.8:

# main.py, requires flask, google-api-python-client
import flask
from googleapiclient import discovery

app = flask.Flask(__name__)
@app.route('/')
def home():
    service = discovery.build('drive', 'v3')

    request = service.files().list()
    response = request.execute()

    return response

On 3.8 the error is

googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/drive/v3/files?alt=json returned "Insufficient Permission: Request had insufficient authentication scopes.">" 

The problem is that google.auth.default() returns an instance of the Compute Engine credentials for Python 3, and that class does not allow changing scopes.

The credentials class uses the metadata service to get an access token. On App Engine standard the metadata service also allows one to request an access token with additional scopes (I haven't checked if this is also possible on Compute Engine or Flex). I have an implementation of ServiceAccountCredentials that supports requesting a token with additional scopes, which works on App Engine standard Python 3.

https://gist.github.com/davidwtbuxton/525924b7f06f56b8530947d55bad1c21

With that code, the service discovery can request the required scopes:

    credentials = ServiceAccountCredentials()
    service = discovery.build('drive', 'v3', credentials=credentials)

It would be cool if we could get this supported using google.auth.default() for Python 3 on App Engine standard. In particular it simplifies a lot of code that may mess around loading credentials from a JSON file or similar.

Would a PR for this feature be accepted?

Thanks,

David

@yoshi-automation yoshi-automation added the triage me I really want to be triaged. label Aug 6, 2020
@busunkim96
Copy link
Contributor

Hi @davidwtbuxton,

I believe Compute Engine is the only one (that uses the metadata server) and prevents additional scopes. IIRC, NodeJS and possibly other language's auth libraries already allow scope changes for this type of credential. I think it would be appropriate to make that change here as well. If you are able to make a PR we would be happy to review. 😄

@busunkim96 busunkim96 added type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. and removed triage me I really want to be triaged. labels Aug 6, 2020
@davidwtbuxton
Copy link
Contributor Author

Thanks for the context @busunkim96 . I will go and test things for Compute Engine and Flex, and come back with a plan for how to add this feature. Obviously it would be great if we can avoid having more special cases.

@davidwtbuxton
Copy link
Contributor Author

First thing I would like to do is change the google.auth.compute_engine._metadata.get(..) request helper to allow arbitrary query parameters.

This would be a useful re-factor so that we can use the get(..) helper to make a request to the metadata service with ?scopes=.. query parameters.

Then we can add support for scoped tokens on Python 3. Here are some ways of doing it:

  1. Change google.auth.app_engine.Credentials so that it uses the metadata service when the app_identity API is not available. This would be weird because that class also supports signing, which I don't think is possible using the metadata service.
  2. Change google.auth.compute_engine.credentials.Credentials so that it allows custom scopes. This would be weird because the ?scopes=.. parameter works on GCE, but it returns a token which ignores the requested scopes. We could add logic to the requires_scopes() and refresh() methods so that the class behaves differently on App Engine vs GCE.
  3. Add a new credentials class specifically for App Engine when the app_identity API is not available (i.e. when running in the Python 3 runtime). We would also need to change the logic for google.auth._default.default(..) to use the new class when appropriate.

I am slightly in favour of option 3.

@mik-laj
Copy link
Contributor

mik-laj commented Aug 9, 2020

I also support option 3. This looks the most elegant.

@dinvlad
Copy link

dinvlad commented Aug 10, 2020

It would be preferable to fix this not just for App Engine, but also for Cloud Run, Cloud Functions, and GKE Workload Identity (all of which support scopes). So we should change the behavior of google.auth.default() such that it always uses scopes when appropriate (either when Credentials are initialized from a SA key or through the metadata server on these platforms).

@busunkim96
Copy link
Contributor

The module naming is a product of the time they were created. google.auth.app_engine.Credentials is only for the older App Engine Python 2.7 runtime. google.auth.compute_engine.credentials is returned in any environment that uses the Metadata Server (Compute Engine, App Engine, Functions, Cloud Run, ...)

I believe GCE is the only one out of the bunch that disallows scope changes, so I vote for 2. NodeJS started to allow scopes in their compute credentials class last year. They have a note stating that it only works in some environments. See code and PR. I think we could do the same here?

The scopes won't be respected by GCE, but should work as desired in other environments.

@davidwtbuxton
Copy link
Contributor Author

@busunkim96 Thanks for pointers to the Node PR. I'll have a go at updating the compute engine credentials.

@dinvlad
Copy link

dinvlad commented Aug 11, 2020

@busunkim96 would it still work for default credentials however, by extension that google.auth.default() will "detect" the corresponding environment and use google.auth.compute_engine.credentials, when appropriate, and use scoped credentials from GOOGLE_APPLICATION_CREDENTIALS when those are set instead? If so, I'd favor 2 as well. Thanks!

@davidwtbuxton
Copy link
Contributor Author

@dinvlad Yes, option 2 would not change the order of tests done in google.auth.default().

@dinvlad
Copy link

dinvlad commented Aug 27, 2020

Any updates on this? Thanks!

@davidwtbuxton
Copy link
Contributor Author

Picking this up again.

gcf-merge-on-green bot pushed a commit that referenced this issue Oct 23, 2020
Part of #579 

This helper is used with '?recursive=true' in one place, and can now be used by
IDTokenCredentials for requests with query parameters to the metadata identity
end-point.

This change will allow making requests to the token end-point with '?scopes=..'
query parameters.
@eyalzek
Copy link

eyalzek commented Oct 23, 2020

I suppose I'm hitting the same issue on a GKE cluster with workload identity enabled. I can successfully authenticate and interact with any GCP services (GCS, BigQuery, etc), but trying to fetch data from Google Analytics fails with:
HttpError: <HttpError 403 when requesting https://analyticsreporting.googleapis.com/v4/reports:batchGet?alt=json returned "Request had insufficient authentication scopes.">.
Settings scopes as per https://google-auth.readthedocs.io/en/latest/user-guide.html#application-default-credentials by passing scopes=['https://www.googleapis.com/auth/analytics.readonly'] results in exactly the same...

@davidwtbuxton
Copy link
Contributor Author

Closing this issue because the original request about using scopes with default credentials is all good. Looks like the user guide in the documentation is currently out-of-date concerning the App Engine runtimes, but that can be moved to a new issue.

https://google-auth.readthedocs.io/en/latest/user-guide.html

Thanks,

David

@upodroid
Copy link

The scopes available to this client depends on the scopes configured on the VM or the GKE Nodepool. Most of the time it is set to cloud-platform so it won't work for non Cloud APIs. You should be able to specify other scopes and it should work.

gcloud beta compute instances set-scopes bastion --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/analytics.readonly --zone europe-west6-b

@dinvlad
Copy link

dinvlad commented Dec 18, 2020

Unfortunately, this doesn't work when using instances with Workflow Identity enabled. The scopes are then supported only through the Metadata API (but not by this library, last time I checked..)

EDIT: OK I see, this was addressed in #633 it seems!

@lululi
Copy link

lululi commented Apr 23, 2021

@davidwtbuxton I seem to still run into this problem in python 37 when using default credentials:

credentials, project = google.auth.default(
            scopes=['https://www.googleapis.com/auth/calendar.readonly'])
service = build("calendar", "v3", credentials=credentials)

When running on app engine standard env(Py3), I'm still getting "Request had insufficient authentication scopes."
Any pointers?

@busunkim96
Copy link
Contributor

@lululi Please open new issue rather than commenting on closed issues (repo maintainers are more likely to miss comments on closed issues).

Please review https://developers.google.com/calendar/auth for authenticating to the Calendar API. It's possible the calendar API method you are using requires additional scopes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

8 participants