1
votes

I am trying to invoke an authenticated HTTP-based cloud function from another cloud function. Let's call them CF1 and CF2 respectively, for the sake of brevity; thus I wish to invoke CF2 from CF1.

Following the example given by the Google Documentation: Authenticating for Invocation, I created a new service account for CF2, and then attached it to CF1 with the roles/cloudfunctions.admin . I downloaded a service key for local testing with Functions Framework, setting it as the Application Default Credentials(ADC); thus CF2 on my local machine connects to CF1 on GCP, authenticating as CF2's service account via ADC.

I have deployed CF1 on Cloud Functions successfully, and was testing whether CF2 on my local machine could reach to CF1 when I was surprised to receive a HTTP 401.

For reference, here is the code in question, which is almost identical to the samples provided by the Google Documentation:

        String serviceUrl = "<cf1-url>";
        GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();

        if (!(credentials instanceof IdTokenProvider)) {
            throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
        }

        IdTokenCredentials tokenCredential =
                IdTokenCredentials.newBuilder()
                        .setIdTokenProvider((IdTokenProvider) credentials)
                        .setTargetAudience(serviceUrl)
                        .build();

        GenericUrl genericUrl = new GenericUrl(serviceUrl);
        HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
        HttpTransport transport = new NetHttpTransport();
        com.google.api.client.http.HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
        com.google.api.client.http.HttpResponse response = request.execute();

I tried referring to:

but I was not able to find a solution to my problem from those questions.

Further testing revealed that the identity token generated via the client SDK: tokenCredential.getIdToken().getTokenValue() is different from the GCloud CLI command gcloud auth print-identity-token. I could use the identity token generated by GCloud CLI to directly invoke CF1 (e.g. via Postman/cURL and authenticated as CF2's service account) but not the identity token printed by the client SDK. This was a surprise as I am using CF 2's service account keys as the ADC, and also authorized it for gcloud access via gcloud auth activate-service-account.

It seems to me that there is no issue with the permissions of the service accounts and cloud functions, as I can directly invoke CF1; thus it would appear to be an issue with the code. However, I am unable to determine the cause of the 401 error.

1
Did you check the principal of the idenitty token generated by your java app? Are you sure that this idenitty is authorized?guillaume blaquiere
Hi @guillaumeblaquiere, sorry I am not that familiar with OAuth. If by principal you are referring to the "audience" field of the credentials, then I have set it to CF1's URL. If by principal you are referring to the user of the identity token, then I had a look at the JWT received and verified via its email field that the token is issued for CF 2's service account, which is a cloud-functions-admin on CF1. And since I can invoke CF1 as CF2's service account via the gcloud CLI, then it appears to me that so far there is no issue with the permissions and identity.Seng Cheong
GCloud and ADC (application default credential) aren't the same. You can have 2 different ones with gcloud (gcloud auth login for gcloud CLI command (like gcloud auth print-identity-token) and gcloud auth application-default login for app and code that use the Google Cloud Client libraries -> your case). That's why, the email of the token generated by your java code is important, you need to be sure that this email is authorized to call the target function.guillaume blaquiere
Hi @guillaumeblaquiere, thanks for your reply. The reason I am sure the email is authorized to access the function, is because I am authenticated on gcloud via gcloud auth activate-service-account, which if i understood correctly from gcloud auth activate-service-account, performs the same function as gcloud auth login, except that I am using service account credentials instead of user account credentials.Seng Cheong
To confirm, I used gcloud config list and verified that the account used in the account field is the service account of CF 2. Thus, it is my understanding that the identity token printed using gcloud auth print-identity-token is indeed the identity token of the CF 2's service account. This service account is also set as the ADC, and the client_email field of the JWT received from Google APIs has also been verified to be the CF2's service account.Seng Cheong

1 Answers

0
votes

The target audience, your serviceURL, must be the raw url, this one provided by the Cloud Functions service.

If you add your parameters (query or path) it won't work.