Authenticating service-to-service

If your architecture is using multiple services, these services likely need to communicate with each other, using either asynchronous or synchronous means. Many of these services may be private and therefore require credentials for access .

For asynchronous communication, you can use the following Google Cloud services:

  • Cloud Tasks for one to one asynchronous communication
  • Pub/Sub for one to many, one to one, and many to one asynchronous communication
  • Cloud Scheduler for regularly scheduled asynchronous communication
  • Eventarc for event-based communication

In all of these cases, the service used manages the interaction with the receiving service, based on the configuration you set up.

But for synchronous communication, your service calls another service directly, over HTTP, using its endpoint URL. For this use case, you should make sure that each service is only able to make requests to specific services. For example, if you have a login service, it should be able to access the user-profiles service, but not the search service.

In this situation, Google recommends that you use IAM and a service identity based on a per-service user-managed service account that has been granted the minimum set of permissions required to do its work.

In addition, the request must present proof of the calling service's identity. To do this, configure your calling service to add a Google-signed OpenID Connect ID token as part of the request.

Set up the service account

To set up a service account, you configure the receiving service to accept requests from the calling service by making the calling service's service account a principal on the receiving service. Then you grant that service account the Cloud Run Invoker ( roles/run.invoker ) role. To do both of these tasks, follow the instructions in the appropriate tab:

Console UI

  1. Go to the Google Cloud console:

    Go to Google Cloud console

  2. Select the receiving service.

  3. Click Show Info Panelin the top right corner to show the Permissionstab.

  4. Click Add principal.

    1. Enter the identity of the calling service. This is usually an email address, by default PROJECT_NUMBER-compute@developer.gserviceaccount.com .

    2. Select the Cloud Run Invoker role from the Select a roledrop-down menu.

    3. Click Save.

gcloud

Use the gcloud run services add-iam-policy-binding command:

gcloud  
run  
services  
add-iam-policy-binding  
 RECEIVING_SERVICE 
  
 \ 
  
--member = 
 'serviceAccount: CALLING_SERVICE_IDENTITY 
' 
  
 \ 
  
--role = 
 'roles/run.invoker' 

where RECEIVING_SERVICE is the name of the receiving service, and CALLING_SERVICE_IDENTITY is the email address of the service account, by default PROJECT_NUMBER-compute@developer.gserviceaccount.com .

Terraform

To learn how to apply or remove a Terraform configuration, see Basic Terraform commands .

Add the following to a google_cloud_run_v2_service resource in your Terraform configuration:
  resource 
  
 "google_cloud_run_v2_service" 
  
 "public" 
  
 { 
  
 name 
  
 = 
  
 "public-service" 
  
 location 
  
 = 
  
 "us-central1" 
  
 deletion_protection 
  
 = 
  
 false 
 # set to "true" in production 
  
 template 
  
 { 
  
 containers 
  
 { 
 # TODO<developer>: replace this with a public service container 
 # (This service can be invoked by anyone on the internet) 
  
 image 
  
 = 
  
 "us-docker.pkg.dev/cloudrun/container/hello" 
 # Include a reference to the private Cloud Run 
 # service's URL as an environment variable. 
  
 env 
  
 { 
  
 name 
  
 = 
  
 "URL" 
  
 value 
  
 = 
  
 google_cloud_run_v2_service.private.uri 
  
 } 
  
 } 
 # Give the "public" Cloud Run service 
 # a service account's identity 
  
 service_account 
  
 = 
  
 google_service_account.default.email 
  
 } 
 } 
 

Replace us-docker.pkg.dev/cloudrun/container/hello with a reference to your container image.

The following Terraform code makes the initial service public.

  data 
  
 "google_iam_policy" 
  
 "public" 
  
 { 
  
 binding 
  
 { 
  
 role 
  
 = 
  
 "roles/run.invoker" 
  
 members 
  
 = 
  
 [ 
  
 "allUsers" 
 , 
  
 ] 
  
 } 
 } 
 resource 
  
 "google_cloud_run_service_iam_policy" 
  
 "public" 
  
 { 
  
 location 
  
 = 
  
 google_cloud_run_v2_service.public.location 
  
 project 
  
 = 
  
 google_cloud_run_v2_service.public.project 
  
 service 
  
 = 
  
 google_cloud_run_v2_service.public.name 
  
 policy_data 
  
 = 
  
 data.google_iam_policy.public.policy_data 
 } 
 

The following Terraform code creates a second Cloud Run service intended to be private .

  resource 
  
 "google_cloud_run_v2_service" 
  
 "private" 
  
 { 
  
 name 
  
 = 
  
 "private-service" 
  
 location 
  
 = 
  
 "us-central1" 
  
 deletion_protection 
  
 = 
  
 false 
 # set to "true" in production 
  
 template 
  
 { 
  
 containers 
  
 { 
 // TODO<developer>: replace this with a private service container 
 // (This service should only be invocable by the public service) 
  
 image 
  
 = 
  
 "us-docker.pkg.dev/cloudrun/container/hello" 
  
 } 
  
 } 
 } 
 

Replace us-docker.pkg.dev/cloudrun/container/hello with a reference to your container image.

The following Terraform code makes the second service private.

  data 
  
 "google_iam_policy" 
  
 "private" 
  
 { 
  
 binding 
  
 { 
  
 role 
  
 = 
  
 "roles/run.invoker" 
  
 members 
  
 = 
  
 [ 
  
 "serviceAccount:${google_service_account.default.email}" 
 , 
  
 ] 
  
 } 
 } 
 resource 
  
 "google_cloud_run_service_iam_policy" 
  
 "private" 
  
 { 
  
 location 
  
 = 
  
 google_cloud_run_v2_service.private.location 
  
 project 
  
 = 
  
 google_cloud_run_v2_service.private.project 
  
 service 
  
 = 
  
 google_cloud_run_v2_service.private.name 
  
 policy_data 
  
 = 
  
 data.google_iam_policy.private.policy_data 
 } 
 

The following Terraform code creates a service account.

  resource 
  
 "google_service_account" 
  
 "default" 
  
 { 
  
 account_id 
  
 = 
  
 "cloud-run-interservice-id" 
  
 description 
  
 = 
  
 "Identity used by a public Cloud Run service to call private Cloud Run services." 
  
 display_name 
  
 = 
  
 "cloud-run-interservice-id" 
 } 
 

The following Terraform code allows services attached to the service account invoke the initial private Cloud Run service.

  data 
  
 "google_iam_policy" 
  
 "private" 
  
 { 
  
 binding 
  
 { 
  
 role 
  
 = 
  
 "roles/run.invoker" 
  
 members 
  
 = 
  
 [ 
  
 "serviceAccount:${google_service_account.default.email}" 
 , 
  
 ] 
  
 } 
 } 
 resource 
  
 "google_cloud_run_service_iam_policy" 
  
 "private" 
  
 { 
  
 location 
  
 = 
  
 google_cloud_run_v2_service.private.location 
  
 project 
  
 = 
  
 google_cloud_run_v2_service.private.project 
  
 service 
  
 = 
  
 google_cloud_run_v2_service.private.name 
  
 policy_data 
  
 = 
  
 data.google_iam_policy.private.policy_data 
 } 
 

Acquire and configure the ID token

After you grant the proper role to the calling service account, follow these steps:

  1. Fetch a Google-signed ID token by using one of the methods described in the following section. Set the audience claim ( aud ) to the URL of the receiving service or a configured custom audience . If you are not using a custom audience, the aud value must remain as the URL of the service, even when making requests to a specific traffic tag .

  2. Add the ID token you fetched from the previous step into one of the following headers in the request to the receiving service:

    • An Authorization: Bearer ID_TOKEN header.
    • An X-Serverless-Authorization: Bearer ID_TOKEN header. You can use this header if your application already uses the Authorization header for custom authorization. This removes the signature before passing the token to the user container.

For other ways to get an ID token that are not described on this page, see Methods for getting an ID token .

Use the authentication libraries

One way to acquire and configure the ID token process is to use the authentication libraries. This code works in any environment, even outside of Google Cloud, where the libraries can obtain authentication credentials for a service account. To use this method, you download a service account key file , and set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the path of the service account key file. For more information, see service account key .

This code does not accept authentication credentials for a user account .

Node.js

  /** 
 * TODO(developer): Uncomment these variables before running the sample. 
 */ 
 // Example: https://my-cloud-run-service.run.app/books/delete/12345 
 // const url = 'https://TARGET_HOSTNAME/TARGET_URL'; 
 // Example (Cloud Run): https://my-cloud-run-service.run.app/ 
 // const targetAudience = 'https://TARGET_AUDIENCE/'; 
 const 
  
 { 
 GoogleAuth 
 } 
  
 = 
  
 require 
 ( 
 ' google-auth-library 
' 
 ); 
 const 
  
 auth 
  
 = 
  
 new 
  
  GoogleAuth 
 
 (); 
 async 
  
 function 
  
 request 
 () 
  
 { 
  
 console 
 . 
 info 
 ( 
 `request 
 ${ 
 url 
 } 
 with target audience 
 ${ 
 targetAudience 
 } 
 ` 
 ); 
  
 const 
  
 client 
  
 = 
  
 await 
  
  auth 
 
 . 
  getIdTokenClient 
 
 ( 
 targetAudience 
 ); 
  
 // Alternatively, one can use `client.idTokenProvider.fetchIdToken` 
  
 // to return the ID Token. 
  
 const 
  
 res 
  
 = 
  
 await 
  
 client 
 . 
 fetch 
 ( 
 url 
 ); 
  
 console 
 . 
 info 
 ( 
 res 
 . 
 data 
 ); 
 } 
 request 
 (). 
 catch 
 ( 
 err 
  
 = 
>  
 { 
  
 console 
 . 
 error 
 ( 
 err 
 . 
 message 
 ); 
  
 process 
 . 
 exitCode 
  
 = 
  
 1 
 ; 
 }); 
 

Python

  import 
  
 urllib 
 import 
  
 google.auth.transport.requests 
 import 
  
 google.oauth2.id_token 
 def 
  
 make_authorized_get_request 
 ( 
 endpoint 
 , 
 audience 
 ): 
  
 """ 
 make_authorized_get_request makes a GET request to the specified HTTP endpoint 
 by authenticating with the ID token obtained from the google-auth client library 
 using the specified audience value. 
 """ 
 # Cloud Run uses your service's hostname as the `audience` value 
 # audience = 'https://my-cloud-run-service.run.app/' 
 # For Cloud Run, `endpoint` is the URL (hostname + path) receiving the request 
 # endpoint = 'https://my-cloud-run-service.run.app/my/awesome/url' 
 req 
 = 
 urllib 
 . 
 request 
 . 
 Request 
 ( 
 endpoint 
 ) 
 auth_req 
 = 
 google 
 . 
 auth 
 . 
 transport 
 . 
 requests 
 . 
 Request 
 () 
 id_token 
 = 
 google 
 . 
 oauth2 
 . 
 id_token 
 . 
 fetch_id_token 
 ( 
 auth_req 
 , 
 audience 
 ) 
 req 
 . 
 add_header 
 ( 
 "Authorization" 
 , 
 f 
 "Bearer 
 { 
 id_token 
 } 
 " 
 ) 
 response 
 = 
 urllib 
 . 
 request 
 . 
 urlopen 
 ( 
 req 
 ) 
 return 
 response 
 . 
 read 
 () 
 

Go

  import 
  
 ( 
  
 "context" 
  
 "fmt" 
  
 "io" 
  
 "google.golang.org/api/idtoken" 
 ) 
 // `makeGetRequest` makes a request to the provided `targetURL` 
 // with an authenticated client using audience `audience`. 
 func 
  
 makeGetRequest 
 ( 
 w 
  
 io 
 . 
 Writer 
 , 
  
 targetURL 
  
 string 
 , 
  
 audience 
  
 string 
 ) 
  
 error 
  
 { 
  
 // Example `audience` value (Cloud Run): https://my-cloud-run-service.run.app/ 
  
 // (`targetURL` and `audience` will differ for non-root URLs and GET parameters) 
  
 ctx 
  
 := 
  
 context 
 . 
 Background 
 () 
  
 // client is a http.Client that automatically adds an "Authorization" header 
  
 // to any requests made. 
  
 client 
 , 
  
 err 
  
 := 
  
 idtoken 
 . 
 NewClient 
 ( 
 ctx 
 , 
  
 audience 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "idtoken.NewClient: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 resp 
 , 
  
 err 
  
 := 
  
 client 
 . 
 Get 
 ( 
 targetURL 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "client.Get: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 defer 
  
 resp 
 . 
 Body 
 . 
 Close 
 () 
  
 if 
  
 _ 
 , 
  
 err 
  
 := 
  
 io 
 . 
 Copy 
 ( 
 w 
 , 
  
 resp 
 . 
 Body 
 ); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 fmt 
 . 
 Errorf 
 ( 
 "io.Copy: %w" 
 , 
  
 err 
 ) 
  
 } 
  
 return 
  
 nil 
 } 
 

Java

  import 
  
 com.google.api.client.http. GenericUrl 
 
 ; 
 import 
  
 com.google.api.client.http. HttpRequest 
 
 ; 
 import 
  
 com.google.api.client.http. HttpResponse 
 
 ; 
 import 
  
 com.google.api.client.http. HttpTransport 
 
 ; 
 import 
  
 com.google.api.client.http.javanet. NetHttpTransport 
 
 ; 
 import 
  
 com.google.auth.http. HttpCredentialsAdapter 
 
 ; 
 import 
  
 com.google.auth.oauth2. GoogleCredentials 
 
 ; 
 import 
  
 com.google.auth.oauth2. IdTokenCredentials 
 
 ; 
 import 
  
 com.google.auth.oauth2. IdTokenProvider 
 
 ; 
 import 
  
 java.io.IOException 
 ; 
 public 
  
 class 
 Authentication 
  
 { 
  
 // makeGetRequest makes a GET request to the specified Cloud Run or 
  
 // Cloud Functions endpoint `serviceUrl` (must be a complete URL), by 
  
 // authenticating with an ID token retrieved from Application Default 
  
 // Credentials using the specified `audience`. 
  
 // 
  
 // Example `audience` value (Cloud Run): https://my-cloud-run-service.run.app/ 
  
 public 
  
 static 
  
  HttpResponse 
 
  
 makeGetRequest 
 ( 
 String 
  
 serviceUrl 
 , 
  
 String 
  
 audience 
 ) 
  
 throws 
  
 IOException 
  
 { 
  
  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 
 
 ( 
 audience 
 ) 
  
 . 
 build 
 (); 
  
  GenericUrl 
 
  
 genericUrl 
  
 = 
  
 new 
  
  GenericUrl 
 
 ( 
 serviceUrl 
 ); 
  
  HttpCredentialsAdapter 
 
  
 adapter 
  
 = 
  
 new 
  
  HttpCredentialsAdapter 
 
 ( 
 tokenCredential 
 ); 
  
  HttpTransport 
 
  
 transport 
  
 = 
  
 new 
  
  NetHttpTransport 
 
 (); 
  
  HttpRequest 
 
  
 request 
  
 = 
  
 transport 
 . 
  createRequestFactory 
 
 ( 
 adapter 
 ). 
  buildGetRequest 
 
 ( 
 genericUrl 
 ); 
  
 return 
  
 request 
 . 
  execute 
 
 (); 
  
 } 
 } 
 

If for some reason you cannotuse the authentication libraries, you can fetch an ID token from the Compute metadata server while your container is running on Cloud Run. Note that this method does not work outside of Google Cloud, including from your local machine.

  curl 
  
 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]" 
  
 \ 
  
 - 
 H 
  
 "Metadata-Flavor: Google" 
 

Where AUDIENCE is the URL of the service you are invoking or a configured custom audience .

The following table summarizes the main parts of a metadata query request:

Components Description
Root URL

All metadata values are defined as sub-paths below the following root URL:

http://metadata.google.internal/computeMetadata/v1
Request header

The following header must be in each request:

Metadata-Flavor: Google

This header indicates that the request was sent with the intention of retrieving metadata values, rather than unintentionally from an insecure source, and lets the metadata server return the data you requested. If you don't provide this header, the metadata server denies your request.

For an end-to-end walkthrough of an application using this service-to-service authentication technique, follow the securing Cloud Run services tutorial .

Use workload identity federation from outside Google Cloud

If your environment uses an identity provider supported by workload identity federation , you can use the following method to securely authenticate to your Cloud Run service from outside Google Cloud:

  1. Set up your service account as described in Set up the service account on this page.

  2. Configure workload identity federation for your identity provider as described in Configuring workload identity federation .

  3. Follow the instructions in Granting external identities permission to impersonate a service account .

  4. Use the REST API to acquire a short-lived token , but instead of calling generateAccessToken to obtain an access token, call generateIdToken instead to get an ID token.

    For example, using cURL:

     ID_TOKEN 
     = 
     $( 
    curl  
    -0  
    -X  
    POST  
    https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/ SERVICE_ACCOUNT 
    :generateIdToken  
     \ 
      
    -H  
     "Content-Type: text/json; charset=utf-8" 
      
     \ 
      
    -H  
     "Authorization: Bearer 
     $STS_TOKEN 
     " 
      
     \ 
      
    -d  
    @-  
    <<EOF  
     | 
      
    jq  
    -r  
    .token  
     { 
      
     "audience" 
    :  
     " SERVICE_URL 
    " 
      
     } 
    EOF ) 
     echo 
      
     $ID_TOKEN 
    

    Where SERVICE_ACCOUNT is the email address of the service account the workload identity pool is configured to access, and SERVICE_URL is the URL of the Cloud Run service you are invoking. This value should remain as the URL of the service, even when making requests to a specific traffic tag . $STS_TOKEN is the Security Token Service token you received in the previous step in the Workload Identity Federation instructions.

You can include the ID token from the previous step in the request to the service by using an Authorization: Bearer ID_TOKEN header or an X-Serverless-Authorization: Bearer ID_TOKEN header. If both headers are provided, only the X-Serverless-Authorization header is checked.

Use a downloaded service account key from outside Google Cloud

If Workload Identity Federation is not appropriate for your environment, you can use a downloaded service account key to authenticate from outside Google Cloud. Update your client code to use the authentication libraries as described previously . For more information, see service account key .

You can acquire a Google-signed ID token by using a self-signed JWT, but this is quite complicated and potentially error-prone. The basic steps are as follows:

  1. Self-sign a service account JWT with the target_audience claim set to the URL of the receiving service or a configured custom audience . If not using custom domains, the target_audience value should remain as the URL of the service, even when making requests to a specific traffic tag .

  2. Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the preceding URL.

  3. Include the ID token in the request to the service by using an Authorization: Bearer ID_TOKEN header or an X-Serverless-Authorization: Bearer ID_TOKEN header. If both headers are provided, only the X-Serverless-Authorization header is checked.

Receive authenticated requests

Within the receiving private service, you can parse the authorization header to receive the information being sent by the Bearer token.

Python

  from 
  
 flask 
  
 import 
 Request 
 from 
  
 google.auth.exceptions 
  
 import 
 GoogleAuthError 
 from 
  
 google.auth.transport 
  
 import 
 requests 
 from 
  
 google.oauth2 
  
 import 
 id_token 
 def 
  
 receive_request_and_parse_auth_header 
 ( 
 request 
 : 
 Request 
 ) 
 - 
> str 
 : 
  
 """Parse the authorization header, validate the Bearer token 
 and decode the token to get its information. 
 Args: 
 request: Flask request object. 
 Returns: 
 One of the following: 
 a) The email from the request's Authorization header. 
 b) A welcome message for anonymous users. 
 c) An error description. 
 """ 
 auth_header 
 = 
 request 
 . 
 headers 
 . 
 get 
 ( 
 "Authorization" 
 ) 
 if 
 auth_header 
 : 
 # Split the auth type and value from the header. 
 auth_type 
 , 
 creds 
 = 
 auth_header 
 . 
 split 
 ( 
 " " 
 , 
 1 
 ) 
 if 
 auth_type 
 . 
 lower 
 () 
 == 
 "bearer" 
 : 
 # Find more information about `verify_token` function here: 
 # https://google-auth.readthedocs.io/en/master/reference/google.oauth2.id_token.html#google.oauth2.id_token.verify_token 
 try 
 : 
 decoded_token 
 = 
 id_token 
 . 
 verify_token 
 ( 
 creds 
 , 
 requests 
 . 
 Request 
 ()) 
 return 
 f 
 "Hello, 
 { 
 decoded_token 
 [ 
 'email' 
 ] 
 } 
 ! 
 \n 
 " 
 except 
 GoogleAuthError 
 as 
 e 
 : 
 return 
 f 
 "Invalid token: 
 { 
 e 
 } 
 \n 
 " 
 else 
 : 
 return 
 f 
 "Unhandled header format ( 
 { 
 auth_type 
 } 
 ). 
 \n 
 " 
 return 
 "Hello, anonymous user. 
 \n 
 " 
 

What's next

Design a Mobile Site
View Site in Mobile | Classic
Share by: