3
votes

We are deviating from datastore scheduled export mechanism (google suggested) and adopting to schedule datastore backup via cloud scheduler which will target HTTP cloud function. Here, we want use cloud function to export our datastore entities to certain storage bucket. The reason for this deviation from standard mechanism is that, we want to avoid duplicated non-app specific code in all our services.

As per docs, the managed export and import service is available only through Datastore mode Admin API (REST, RPC) and requests require OAuth 2.0 authorization.

In cloud function, to access datastore API https://datastore.googleapis.com/v1/projects/<APP ID>:export, we require access_token from scope https://www.googleapis.com/auth/datastore.

In standard GAE application code, using python27 runtime, we can get access_token as per below example -

from google.appengine import app_identity

access_token, _ = app_identity.get_access_token('https://www.googleapis.com/auth/datastore')

But, cloud functions have Python37 runtime. So, importing google.appengine gives error as error as error: ModuleNotFoundError: No module named 'google.appengine'

How can we get access_token for the required scope? (any one of below scopes) -

Please suggest reference to Python code/document. Thanks.

2
I read that library oauth2client is deprecated and should use google.oauth2 instead but, still an issue. Cloud function throws an error module 'google' has no attribute 'oauth2'Prashant Jamkhande
Datastore export and import can also be done via client API (Python). Below is the example,Prashant Jamkhande

2 Answers

4
votes

JWT and Access Token are two separate things. JWT is composed of three parts ie a header, a claim set, and a signature. On the other hand, an access token can only be obtained from the Authorization Server. Please 'Addendum' section of this google document. I created a cloud function by passing service account details to encode into JWT( with a header and payload).

signed_jwt = jwt.encode(payload, key, headers=additional_headers, algorithm='RS256')
decoded_signed_jwt = signed_jwt.decode("utf-8")

In order to obtain the access token, please refer the following code snippet.

token_endpoint = 'https://www.googleapis.com/oauth2/v4/token'
token_req_data = {
    'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    'assertion': decoded_signed_jwt
}
token_post_data = urllib.parse.urlencode(token_req_data).encode("utf-8")

access_token_req = urllib.request.Request(token_endpoint, token_post_data)
access_token_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
result = urllib.request.urlopen(access_token_req)
json_str = result.read()

This json string should include access token. Use this access token in request header of for required API. Please update your payload for signed JWT as below, otherwise you may not be able to get a valid JWT.

'scope': 'https://www.googleapis.com/auth/cloud-platform',
'aud': 'https://www.googleapis.com/oauth2/v4/token'

Hope this helps and I thank to Google support for some part of this answer.

1
votes

Python 3.4 or higher is supported by google.appengine. The error is releated to google-api-python-client not being installed as a dependency when the Function runs.

Try adding it to requeriments.txt on the Clound Functions editor page.

You can also use google-auth by import google.auth