Search code examples
google-app-enginegoogle-cloud-functionsgoogle-authentication

How can I call a Google Cloud Function from Google App Engine?


I have an App Engine project.

I also have a Google Cloud Function.

And I want to call that Google Cloud Function from the App Engine project. I just can't seem to get that to work.

Yes, if I make the function full public (i.e. set the Cloud Function to 'allow all traffic' and create a rule for 'allUsers' to allow calling the function) it works. But if I limit either of the two settings, it stops working immediately and I get 403's.

The App and Function are in the same project, so I would at least assume that setting the Function to 'allow internal traffic only' should work just fine, provided that I have a rule for 'allUsers' to allow calling the function.

How does that work? How does one generally call a (non-public) Google Cloud Function from Google App Engine?


Solution

  • You need an auth header for the ping to the function url. It should look like:

    headers = {
        ....
        'Authorization': 'Bearer some-long-hash-token'
    }
    

    Here is how to get the token:

    import requests
    token_response = requests.get(
        'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=' +
        'https://[your zone]-[your app name].cloudfunctions.net/[your function name]', 
        headers={'Metadata-Flavor': 'Google'})
        
    return token_response.content.decode("utf-8")
    

    'Allow internal traffic only' does not work as expected. My App Engine app is in the same project as the Functions, and it does not work. I had to turn on 'Allow all traffic', and use the header method.

    Example:

    def get_access_token():
        import requests
        token_response = requests.get(
            'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=' +
            'https://us-central1-my_app.cloudfunctions.net/my_function', 
            headers={'Metadata-Flavor': 'Google'})
            
        return token_response.content.decode("utf-8")
        
    def test():
        url_string = f"https://us-central1-my_app.cloudfunctions.net/my_function?message=it%20worked"
        
        access_token = get_access_token()
        
    
        print(
            requests.get(url_string, headers={'Authorization': f"Bearer {access_token}"}
        )