Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

Apigee To Cloud function Auth

We have an apigeeX proxy calling cloud function. Apigee proxy is deployed using service account X and this service account has appropriate role to invoke the cloud function. I am wondering if by default apigee passes service account credentials when it calls cloud function or we need to configure our proxy to obtain the credentials and pass it to CF through proxy code

Client (User Auth)=> Apigee => (Service Auth??) Cloud Function

 
3 8 2,431
8 REPLIES 8

You need to use the Authentication element in the TargetEndpoint, and specify a GoogleIDToken. 

EDIT - here is a sample that sets up Apigee X to connect to a cloud function.

I just tried this.  Here's how I set it up.  In my setup I have 2 GCP projects: one that runs the Cloud Function, and one that is the host for the Apigee X organization.  I've set up my Cloud Function to use nodejs v18, and to run as the default service account. Like this: 

 

CF_PROJECT=my-cf-project
CF_NAME=dchiesa-cf101
REGION=us-west1

gcloud functions deploy $CF_NAME \
  --gen2 \
  --project $CF_PROJECT \
  --runtime=nodejs18 \
  --region=$REGION \
  --source=. \
  --entry-point=helloGET \
  --trigger-http 

 

And it does not permit unauthenticated access. Which means it's going to require an ID Token , passed as a bearer token. Because I am editor/owner on this project, I have the required permission to invoke this cloud function.  Therefore I can test this with an ID token that identifies myself, like this: 

 

curl -i -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
   https://us-west1-my-cf-project.cloudfunctions.net/dchiesa-cf101/helloGET

 

And that works.

But I want (you want) a service account to be able to invoke this function.  So I need to add a Service Account that has the appropriate permission to invoke this Cloud Function. I've created the Service Account in the Apigee project, which, remember, is a different project than the one that hosts the cloud function.

 

SA_NAME=cf101-invoker
SA_PROJECT=my-apigee-project
gcloud iam service-accounts create $SA_NAME --project "$SA_PROJECT" 

SA_EMAIL=${SA_NAME}@${SA_PROJECT}.iam.gserviceaccount.com
gcloud functions add-invoker-policy-binding $CF_NAME \
   --region="$REGION" \
   --project="$CF_PROJECT" \
   --member="serviceAccount:${SA_EMAIL}"

 

To test this, I'll need to have permission to get an Identity token that identifies that service account.  So: 

 


gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
    --project $SA_PROJECT \
    --member=user:my_user@organization.com  \
    --role=roles/iam.serviceAccountUser 

 

And having that permission I can request an ID token: 

 

audience="https://${REGION}-${CF_PROJECT}.cloudfunctions.net/${CF_NAME}"
curl -X POST \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -d '{"audience": "'${audience}'", "includeEmail": "true"}' \
    "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SA_EMAIL}:generateIdToken"

 

The result of that is an ID token, which I can pass as a bearer to the cloud function: 

 

SA_IDTOKEN=ey...output-of-the-above

curl -i -H "Authorization: Bearer ${SA_IDTOKEN}" \
   https://us-west1-my-cf-project.cloudfunctions.net/dchiesa-cf101/helloGET

 

And that works. Good. 

Now, to tell Apigee to do the authentication magic, using that service account, I use this TargetEndpoint configuration: 

 

 

<TargetEndpoint name='target-1'>
  ...
  <HTTPTargetConnection>
    <Authentication>
      <GoogleIDToken>
        <!--
         This did not work for me
        <Audience useTargetUrl="true"/>
        -->
        <Audience>https://us-west1-my-cf-project.cloudfunctions.net/dchiesa-cf101</Audience>
      </GoogleIDToken>
    </Authentication>

    <SSLInfo>
      <Enabled>true</Enabled>
      <Enforce>true</Enforce>
    </SSLInfo>

    <!-- the proxy.pathsuffix will get appended -->
    <URL>https://us-west1-my-cf-project.cloudfunctions.net/dchiesa-cf101</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

 

 

And I must deploy the API Proxy with that service account.  And then to invoke that API Proxy, I pass in the /helloGET as the proxy pathsuffix., which gets added to the URL for the target, and the right thing happens. It all works. 

Full tutorial for using Apigee X to connect to cloud functions: https://github.com/GoogleCloudPlatform/apigee-samples/tree/main/cloud-functions

 

Hi @dchiesa1 ,

So we have created the Service Account in ApigeeX Project and then we have cloud functions generation 2 on another project and we are using PSC .

Just want to understand more,

1. Do we need the cloud function project to provide access to our created service account ?

2. If no, how does the GoogleIdToken in Apigee works ? Does that mean we can create a single Service Account to call any cloud functions backend just give it a correct Audience ? Please correct me if I am missing something or my understanding is incorrect.

Thank you.

Do we need the cloud function project to provide access to our created service account ?

yes. Specifically, Grant the permission to invoke the function, to the Service Account . The service account exists in the Apigee X project.

This gcloud command should do it:

 

gcloud functions add-invoker-policy-binding "$CLOUD_FUNCTION_NAME" \
  --member="serviceAccount:${SA_EMAIL}" \
  --region="$CLOUD_FUNCTIONS_REGION" \
  --project="$CLOUD_FUNCTIONS_PROJECT"

You may need to wait a bit here, perhaps 30 seconds, for that change to take effect. Updating IAM policy is an "eventually consistent" operation. After the permissions get propagated, you can invoke the proxy and it should have access to the cloud function.

 

Hi @dchiesa1 , I am getting stuck at granting the roles - i.e  granting SA "cf101-invoker" ability to invoke a CF hosted in a different project - "my-cf-project"

Would you be able to give me steps that i can follow on the GCP UI ?

Here are the steps -

Navigate to Cloud Functions -> Click on your cloud Function -> navigate to the "Permissions" tab -> Click on Grant Access -> Enter the APIGEE SA email -> select the role "Cloud Functions Invoker".

Hope it helps someone !!

Hi! I'm using Apigee X in one project (Project A) to call a Cloud Run service hosted in another project (Project B).

In the TargetEndpoint, I’ve configured:
<Authentication>
<GoogleIDToken>
<Audience>https://api.my-service.example.com</Audience>
</GoogleIDToken>
</Authentication>

I also added a ClearAuthorizationHeader policy in the PreFlow to remove any incoming Authorization header from the client.

The service account used by Apigee has roles/run.invoker on the Cloud Run service, and I’ve confirmed it works when testing manually via:

Even with the above config, Apigee is still forwarding the client's access_token (e.g., an OAuth token) to the backend, not the ID token generated from the service account.

I was expecting Apigee to:

  1. Strip the original Authorization header (via ClearAuthorizationHeader)

  2. Inject a new one like Authorization: Bearer <ID_TOKEN> using the configured SA

But that's not happening — the original token remains.

What I’ve tried:

  • Confirmed the ClearAuthorizationHeader step is in the PreFlow → Request before the actual routing happens.

  • Validated that the Service Account has the correct roles/run.invoker.

  • Verified that the backend Cloud Run service expects authentication via ID Token (Require authentication is enabled).


Is there any step I'm missing to make Apigee inject the new token properly?

Any help is appreciated — thanks in advance!




I suspect you have a faulty XML configuration somehow. The elements are misplaced, or your target isn't being invoked at all. 

What you expect to happen... is what should happen, if you have it configured properly. 

You didn't show your TargetEndpoint, only a fragment.  The required element hierarchy is:

TargetEndpoint / HTTPTargetConnection / Authentication / GoogleIDToken / Audience

Do you have it set like this? 

Have you tried running apigeelint on your proxy to see if there are any  obvious problems? 

You do not actually NEED the ClearAuthorizationHeader step.  The TargetEndpoint will overwrite the header appropriately. It's not WRONG to have the ClearAuthorizationHeader.  But it is redundant. 

The observations you shared suggest one of these as a cause

  • the target endpoint is not being used.  (Have you checked your RouteRule? )
  • the XML configuration within the TargetEndpoint is wrong.

The issue was that the backend didn’t recognize the token sent by the service account via Apigee because it was prefixed with Bearer, while the backend was expecting the raw JWT only. After adjusting the backend to accept tokens with the Bearer prefix, authentication worked correctly.

Thanks a lot for the help!