Search code examples
google-app-enginegoogle-cloud-platformnext.jsgoogle-iapidentity-aware-proxy

Service to service requests on App Engine with IAP


I'm using Google App Engine to host a couple of services (a NextJS SSR service and a backend API built on Express). I've setup my dispatch.yaml file to route /api/* requests to my API service and all other requests get routed to the default (NextJS) service.

dispatch:
  - url: '*/api/*'
    service: api

The problem: I've also turned on Identity-Aware Proxy for App Engine. When I try to make a GET request from my NextJS service to my API (server-side, via getServerSideProps) it triggers the IAP sign-in page again instead of hitting my API. I've tried out a few ideas to resolve this:

  1. Forwarding all cookies in the API request
  2. Setting the X-Requested-With header as mentioned here
  3. Giving IAP-secured Web App User permissions to my App Engine default service account

But nothing seems to work. I've confirmed that turning off IAP for App Engine allows everything to function as expected. Any requests to the API from the frontend also work as expected. Is there a solution I'm missing or a workaround for this?


Solution

  • You need to perform a service to service call. That's no so simple and you have not really example for that. Anyway I tested (in Go) and it worked.

    Firstly, based your development on the Cloud Run Service to Service documentation page.

    You will have this piece of code in NodeJS sorry, I'm not a NodeJS developer and far least a NexJS developer, you will have to adapt

    // Make sure to `npm install --save request-promise` or add the dependency to your package.json
    const request = require('request-promise');
    
    const receivingServiceURL = ...
    
    // Set up metadata server request
    // See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
    const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
    const tokenRequestOptions = {
        uri: metadataServerTokenURL + receivingServiceURL,
        headers: {
            'Metadata-Flavor': 'Google'
        }
    };
    
    // Fetch the token, then provide the token in the request to the receiving service
    request(tokenRequestOptions)
      .then((token) => {
        return request(receivingServiceURL).auth(null, null, true, token)
      })
      .then((response) => {
        res.status(200).send(response);
      })
      .catch((error) => {
        res.status(400).send(error);
      });    
    

    This example won't work because you need the correct audience. Here, the variable is receivingServiceURL. It's correct for Cloud Run (and Cloud Functions) but not for App Engine behind IAP. You need to use the Client ID of the OAuth2 credential named IAP-App-Engine-app

    Ok, hard to understand what I'm talking about. So, go to the console, API & Services -> Creentials. From there, you have a OAuth2 Client ID section. copy the Client ID column of the line IAP-App-Engine-app, like that

    enter image description here

    Final point, be sure that your App Engine default service account has the authorization to access to IAP. And add it as IAP-secured Web App User. The service account has this format <PROJECT_ID>@appspot.gserviceaccount.com

    Not really clear also. So, go to the IAP page (Security -> Identity Aware Proxy), click on the check box in front of App Engine and go the right side of the page, in the permission panel

    enter image description here


    In the same time, I can explain how to deactivate IAP on a specific service (as proposed by NoCommandLine). Just a remark: deactivate security when you have trouble with it is never a good idea!!

    Technically, you can't deactive IAP on a service. But you can grant allUsers as IAP-secured Web App User on a specific service (instead of clicking on the checkbox of App Engine, click on the checkbox of a specific service). And like that, even with IAP you authorized all users to access to your service. it's an activation without checks in fact.