2

I'm trying to generate a id_token to make authenticated calls to Google Cloud Functions using this following code, that I got from here:

# IAP audience is the ClientID of IAP-App-Engine-app in 
# the API->credentials page
# Cloud Function and Cloud Run need the base URL of the service
audience = 'my_cloud_function_url'
# #1 Get the default credential to generate the access token
credentials, project_id = google.auth.default()

# #2 To use the current service account email
service_account_email = credentials.service_account_email

# Don't work with user account, so define manually the email
# service_account_email = 'MY SERVICE ACCOUNT EMAIL'
# #3 prepare the call the the service account credentials API
sa_credentials_url =  f'https://iamcredentials.googleapis.com/' \
                      f'v1/projects/-/serviceAccounts/'  \
                      f'{service_account_email}:generateIdToken'
headers = {'Content-Type': 'application/json'}
# Create an AuthorizedSession that includes 
# automatically the access_token based on your credentials
authed_session = AuthorizedSession(credentials)
# Define the audience in the request body
# add the parameter "'includeEmail':true" for IAP access
body = json.dumps({'audience': audience})
# Make the call 
token_response = authed_session.request('POST',sa_credentials_url,
                                        data=body, headers=headers)

jwt = token_response.json()
id_token = jwt['token']

I'm not running it into a Google Cloud environment, so I set my GOOGLE_APPLICATION_CREDENTIALS to a json file from a service account I generated in the console. I asked my network manager to authorize the service account within the ['https://www.googleapis.com/auth/cloud-platform'] scope.

When I run the script, I get the following error:

{'error': {'code': 403, 'message': 'The caller does not have permission', 'status': 'PERMISSION_DENIED'}}

I imagine that this error has something to do with not having some intern access with this service account, but I'm not sure, can anybody help me with this?

Edit:

As stated by @guillaume blaquiere on the comments, you can get the id_token without calling the Service Account Credential API, And I could obtain the id_token, but was unable to call my service account with that id_token

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

request = google.auth.transport.requests.Request()
audience = 'my_cloud_function_url'

id_token = google.oauth2.id_token.fetch_id_token(request, audience)
headers = {'Authorization': f'bearer {id_token}'}
service_response = requests.get(audience, headers=headers)

Does anyone knows why thes id_token is invalid?

Hugo-Cruz
  • 88
  • 1
  • 8

2 Answers2

4

You don't need to call the Service Account Credential API, you can use the fetch_id_token method (but you have to know which it exists! It's not very well known and documented!)

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

request = google.auth.transport.requests.Request()
audience = 'my_cloud_function_url'

id_token = google.oauth2.id_token.fetch_id_token(request, audience)

The library use the default credential that you set in the GOOGLE_APPLICATION_CREDENTIALS env vars. So, it's convenient to get an ID token with this python lib!

guillaume blaquiere
  • 66,369
  • 2
  • 47
  • 76
  • I tried this and obtained an id_token (which was the same that I obtained from the gcloud auth print-identity-token command on the console), but was not able to access my cloud function with this token, do you know why? – Hugo-Cruz Apr 16 '21 at 17:09
  • What's the audience that you put? can you share it (hide your projectID)? And how do you perform the call to the Cloud Function after having generated the token? – guillaume blaquiere Apr 16 '21 at 18:12
  • I'm using the cloud function's trigger: https://region-name-project-id.cloudfunctions.net/cf-name for calling the cf i'm using: headers = {'Authorization': f'bearer {id_token}'} service_response = requests.get(cf_trigger_url, headers=headers) – Hugo-Cruz Apr 16 '21 at 18:44
  • Do you get 403 or 401? – guillaume blaquiere Apr 16 '21 at 18:56
  • I got 403 to this – Hugo-Cruz Apr 16 '21 at 19:27
  • Works great on my side. Do you have a load balancer or an additional layer like that? Can you edit your question and paste your new code, the request to the Cloud Functions included? – guillaume blaquiere Apr 16 '21 at 20:09
  • I edited the answer with the question. I don't know if there's an adittional layer on my gcp project, all I know is that the service account that I'm using only has the "Viewer" role, is it because of that? – Hugo-Cruz Apr 16 '21 at 20:30
  • Yes, put your service account Cloud Function invoker. But normally, if you aren't authorized, you should received a 401, and not a 403, that's why it's strange!! – guillaume blaquiere Apr 16 '21 at 21:44
  • In case you are new to this google oauth area like me, this may be helpful to you. --- On `how to get value set for GOOGLE_APPLICATION_CREDENTIALS`, view [here](https://stackoverflow.com/a/45862383/248616) os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'path_to_your/credential_file.json' --- On `what pip package to install`, view [here](https://stackoverflow.com/a/50418970/248616) > pip install --upgrade google-auth – Nam G VU Jan 12 '22 at 05:45
2

The 403 error The caller does not have permission means that the running service account of your Cloud Function doesn't have the required role to invoke the function. Could you go to IAM and check if your service account has Cloud Functions Invoker role? If the service account doesn't have the permission, then please follow this step:

  1. Check the role of your Service Account in IAM, look for Role column and verify if it doesn't have the role mentioned above.
  2. To add permission, hover to your Service Account then Click edit in the right side column named Inheritance.
  3. Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
  4. Click Save.
JM Gelilio
  • 3,482
  • 1
  • 11
  • 23
  • Can you clarify which service account I have to ask for authorization? I'm not using the standard service account of my gcp project, I asked the manager to create another one, and that's the one I'm using to try to obtain the id_token. – Hugo-Cruz Apr 16 '21 at 17:07
  • @Hugo-Cruz, ask your manager to give you a Service Account that has a role of Cloud Function Invoker. – JM Gelilio Apr 19 '21 at 14:18