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:
- Google Cloud Platform - cloud functions API - 401 Unauthorized
- Cloud Function Permissions (403 when invoking from another cloud function)
- Google Cloud Function Authorization Failing
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.
gcloud auth login
for gcloud CLI command (like gcloud auth print-identity-token) andgcloud 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 blaquieregcloud auth activate-service-account
, which if i understood correctly from gcloud auth activate-service-account, performs the same function asgcloud auth login
, except that I am using service account credentials instead of user account credentials. – Seng Cheonggcloud config list
and verified that the account used in theaccount
field is the service account of CF 2. Thus, it is my understanding that the identity token printed usinggcloud 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 theclient_email
field of the JWT received from Google APIs has also been verified to be the CF2's service account. – Seng Cheong