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

apigeex calling cloud function via ILB protected by IAP - Invalid IAP credentials: JWT email claim

Hi @dchiesa1 @kurtkanaskie 

We are trying to call a cloud function from apigeex via ILB protected by IAP. Currently we are seeing an error "Invalid IAP credentials: JWT 'email' claim isn't a string". Is there a shared flow or function that could generate JWT for IAP ? apigeex service account has been provided access in the "IAP: Secured Web App User" in the destination GCP project. could you please help on how to generate JWT from apigeex end?

thanks!!

Raghu 

Solved Solved
0 1 428
1 ACCEPTED SOLUTION

Invoking a Cloud Run or a Cloud Function through IAP, looks like this:

Screenshot 2024-09-16 at 4.50.24 PM.png

source: https://cloud.google.com/iap/docs/concepts-overview

Effectively you want IAP to receive an inbound authenticated request from Apigee, and then verify that identity, and if it is correct/valid, invoke the cloud Function (Cloud Run). Apigee needs to send a specially-formatted JWT to IAP in that request.

Is there a shared flow or function that could generate JWT for IAP ? apigeex service account has been provided access in the "IAP: Secured Web App User" in the destination GCP project. could you please help on how to generate JWT from apigeex end?

I recommend that you do not try to "manually" construct the JWT with a sharedflow or function etc. Apigee can generate the JWT for you in the correct format, if you use the Authentication element in the HTTPTargetConnection.

Here's what you need to do:

  1. configure IAP to be able to invoke your Cloud Run service (Cloud Functions gen2 works the same way)
    # get the service account for IAP
    IAP_SA_EMAIL=service-${PROJECT_NUMBER}@gcp-sa-iap.iam.gserviceaccount.com
    # allow it to invoke the service
    gcloud run services add-iam-policy-binding "${CLOUD_RUN_SERVICE_NAME}" \
      --member="serviceAccount:${IAP_SA_EMAIL}" \
      --role='roles/run.invoker' \
      --region="${REGION}" \
      --project="$PROJECT"
    ​
  2. Find the backend service
    gcloud compute backend-services list
    ​BACKEND_SERVICE=....one.of.the.values.from.the.above....
  3. allow the Proxy's SA to invoke the backend service which is protected by IAP
    # create a new SA that the proxy will run as *(optional if you already have one)
    PROXY_SERVICE_ACCOUNT_NAME=my-apigee-proxy-sa
    gcloud iam service-accounts create "${PROXY_SERVICE_ACCOUNT_NAME}"
    PROXY_SERVICE_ACCOUNT_EMAIL="${PROXY_SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
    
    # allow the SA to invoke the endpoint protected by IAP 
    gcloud iap web add-iam-policy-binding --service "${BACKEND_SERVICE}" \
      --member="serviceAccount:${PROXY_SERVICE_ACCOUNT_EMAIL}" \
      --role='roles/iap.httpsResourceAccessor' \
      --resource-type=backend-services \
      --project="$PROJECT_ID"
    
  4. determine the audience you need on the JWT coming from Apigee. The Audience required is the OAuth2 ClientID for the IAP. This is something you should have previously set up, when you configured the OAuth2 consent screen! If you haven't done that, then go do that first.
    gcloud compute backend-services describe "${BACKEND_SERVICE}" \
       --project="${PROJECT_ID}" \
       --global \
       --format="value(iap.oauth2ClientId)"
    
  5. In your targetendpoint, specify the Authentication element, with the right Audience and IncludeEmail = true:
      ...
     <HTTPTargetConnection>
       <Authentication>
        <GoogleIDToken>
          <!-- replace this with the value you obtained above -->
          <Audience>511582533367-vtrfdsroh4iv.apps.googleusercontent.com</Audience>
          <IncludeEmail>true</IncludeEmail>
        </GoogleIDToken>
       </Authentication>
       <URL>https://DNS-name-for-load-balancer.com/</URL>
     </HTTPTargetConnection>
    
    

    This configuration tells Apigee to generate an ID token, and send it to the target as a Bearer token in the Authorization header.

  6. Then import and deploy your API proxy with the proxy Service Account

When you send a request to the proxy, Apigee generates a JWT and sends it to the target, which is an IAP-protected endpoint. IAP checks the Apigee-provided Identity, then invokes Cloud Run (Functions). Cloud Run then checks the IAP-provided identity. For maximum security, the cloud run service or cloud function should also check the Apigee-generated identity! But this is done in your own code, so it will vary depending on the language you use.

View solution in original post

1 REPLY 1

Invoking a Cloud Run or a Cloud Function through IAP, looks like this:

Screenshot 2024-09-16 at 4.50.24 PM.png

source: https://cloud.google.com/iap/docs/concepts-overview

Effectively you want IAP to receive an inbound authenticated request from Apigee, and then verify that identity, and if it is correct/valid, invoke the cloud Function (Cloud Run). Apigee needs to send a specially-formatted JWT to IAP in that request.

Is there a shared flow or function that could generate JWT for IAP ? apigeex service account has been provided access in the "IAP: Secured Web App User" in the destination GCP project. could you please help on how to generate JWT from apigeex end?

I recommend that you do not try to "manually" construct the JWT with a sharedflow or function etc. Apigee can generate the JWT for you in the correct format, if you use the Authentication element in the HTTPTargetConnection.

Here's what you need to do:

  1. configure IAP to be able to invoke your Cloud Run service (Cloud Functions gen2 works the same way)
    # get the service account for IAP
    IAP_SA_EMAIL=service-${PROJECT_NUMBER}@gcp-sa-iap.iam.gserviceaccount.com
    # allow it to invoke the service
    gcloud run services add-iam-policy-binding "${CLOUD_RUN_SERVICE_NAME}" \
      --member="serviceAccount:${IAP_SA_EMAIL}" \
      --role='roles/run.invoker' \
      --region="${REGION}" \
      --project="$PROJECT"
    ​
  2. Find the backend service
    gcloud compute backend-services list
    ​BACKEND_SERVICE=....one.of.the.values.from.the.above....
  3. allow the Proxy's SA to invoke the backend service which is protected by IAP
    # create a new SA that the proxy will run as *(optional if you already have one)
    PROXY_SERVICE_ACCOUNT_NAME=my-apigee-proxy-sa
    gcloud iam service-accounts create "${PROXY_SERVICE_ACCOUNT_NAME}"
    PROXY_SERVICE_ACCOUNT_EMAIL="${PROXY_SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
    
    # allow the SA to invoke the endpoint protected by IAP 
    gcloud iap web add-iam-policy-binding --service "${BACKEND_SERVICE}" \
      --member="serviceAccount:${PROXY_SERVICE_ACCOUNT_EMAIL}" \
      --role='roles/iap.httpsResourceAccessor' \
      --resource-type=backend-services \
      --project="$PROJECT_ID"
    
  4. determine the audience you need on the JWT coming from Apigee. The Audience required is the OAuth2 ClientID for the IAP. This is something you should have previously set up, when you configured the OAuth2 consent screen! If you haven't done that, then go do that first.
    gcloud compute backend-services describe "${BACKEND_SERVICE}" \
       --project="${PROJECT_ID}" \
       --global \
       --format="value(iap.oauth2ClientId)"
    
  5. In your targetendpoint, specify the Authentication element, with the right Audience and IncludeEmail = true:
      ...
     <HTTPTargetConnection>
       <Authentication>
        <GoogleIDToken>
          <!-- replace this with the value you obtained above -->
          <Audience>511582533367-vtrfdsroh4iv.apps.googleusercontent.com</Audience>
          <IncludeEmail>true</IncludeEmail>
        </GoogleIDToken>
       </Authentication>
       <URL>https://DNS-name-for-load-balancer.com/</URL>
     </HTTPTargetConnection>
    
    

    This configuration tells Apigee to generate an ID token, and send it to the target as a Bearer token in the Authorization header.

  6. Then import and deploy your API proxy with the proxy Service Account

When you send a request to the proxy, Apigee generates a JWT and sends it to the target, which is an IAP-protected endpoint. IAP checks the Apigee-provided Identity, then invokes Cloud Run (Functions). Cloud Run then checks the IAP-provided identity. For maximum security, the cloud run service or cloud function should also check the Apigee-generated identity! But this is done in your own code, so it will vary depending on the language you use.

Top Solution Authors