Authenticating for invocation

To create, update, or perform other administrative actions on a function, you must have an account that is a member that has an appropriate role. See Authorizing Access via IAM for more information.

But invoking a function can be a more complex event. Background functions can only be invoked by the event source to which they are subscribed, but HTTP functions can be invoked by different kinds of identities, originating in different places. The invoker might be a developer who is testing the function or another function or service that wishes to use the function. By default, these identities must authenticate themselves (provide proof of their identity) as well as have the appropriate permissions. Unauthenticated access is possible, but must be enabled. See Managing Access via IAM for more information.

Authenticating Developer Testing

As a developer, you need access to create, update, and delete functions, and this is granted using the normal (IAM) process.

But as a developer you are also likely to need to invoke your functions for testing purposes. To invoke a function using curl or similar tools, you must:

  • Have the account you are using to access Cloud Functions assigned a role that contains the cloudfunctions.functions.invoke permission. By default, the Cloud Functions Admin and Cloud Functions Developer roles have this permission. See Cloud Functions IAM Roles for the full list of roles and their associated permissions.

  • If you are working from your local machine, set up command line access by initializing the Google Cloud SDK. Make sure you have downloaded the service account key and set your GOOGLE_APPLICATION_CREDENTIALS environment variable.

  • Provide authentication credentials in your request as a Google-generated ID token stored in an Authorization header. For example, you might get a token via gcloud as follows:

    curl https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME \
      -H "Authorization: bearer $(gcloud auth print-identity-token)"
    

As always, we recommend that you allocate the minimum set of permissions required to develop and use your functions. Make sure that IAM policies on your functions are limited to the minimum number of users and service accounts.

Authenticating function to function calls

When building services that connect multiple functions, it's a good idea to ensure that each function can only send requests to a specific subset of your other functions. For instance, if you have a login function, it should be able to access the user profiles function, but it probably shouldn't be able to access the search function.

To configure the receiving function to accept requests from a specific calling function, you need to add the calling function's service account as a member on the receiving function and grant that member the Cloud Functions Invoker (roles/cloudfunctions.invoker) role. The process is the same as adding any other member to the function.

Console

  1. Go to the Google Cloud Console:

    Go to Google Cloud Console

  2. Click the checkbox next to the receiving function.

  3. Click Permissions at the top of the screen. The Permissions panel opens.

  4. Click Add member.

  5. In the New members field, enter the identity of the calling function. This should be a service account email.

  6. Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.

  7. Click Save.

GCloud

Use the gcloud functions add-iam-policy-binding command:

gcloud functions add-iam-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY' \
  --role='roles/cloudfunctions.invoker'

where RECEIVING_FUNCTION is the name of the receiving function and CALLING_FUNCTION_IDENTITY is the calling function identity, a service account email.

Because it will be invoking the receiving function, the calling function must also provide a Google-signed ID token to authenticate. This is a two step process:

  1. Create a Google-signed ID token with the audience field (aud) set to the URL of the receiving function.

  2. Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the function.

By far the easiest and most reliable way to manage this process is to use the authentication libraries, as shown below, to generate and employ this token. This code works anywhere the libraries can authenticate, including using Application Default Credentials locally.

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const url = 'https://TARGET_URL';
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();

async function request() {
  if (!targetAudience) {
    // Use the request URL hostname as the target audience for requests.
    const {URL} = require('url');
    targetAudience = new URL(url).origin;
  }
  console.info(`request ${url} with target audience ${targetAudience}`);
  const client = await auth.getIdTokenClient(targetAudience);
  const res = await client.request({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(service_url):
    """
    make_authorized_get_request makes a GET request to the specified HTTP endpoint
    in service_url (must be a complete URL) by authenticating with the
    ID token obtained from the google-auth client library.
    """

    req = urllib.request.Request(service_url)

    auth_req = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(auth_req, service_url)

    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.
func makeGetRequest(w io.Writer, targetURL string) error {
	// functionURL := "https://TARGET_URL"
	ctx := context.Background()

	// client is a http.Client that automatically adds an "Authorization" header
	// to any requests made.
	client, err := idtoken.NewClient(ctx, targetURL)
	if err != nil {
		return fmt.Errorf("idtoken.NewClient: %v", err)
	}

	resp, err := client.Get(targetURL)
	if err != nil {
		return fmt.Errorf("client.Get: %v", err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(w, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %v", 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.
  public static HttpResponse makeGetRequest(String serviceUrl) 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(serviceUrl)
            .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();
  }
}

Generating tokens manually

If you're invoking a function and for some reason you cannot use the authentication libraries, there are two ways you can get the ID token manually, either using the Compute metadata server or by creating a self-signed JWT and exchanging it for a Google-signed ID token.

Using the metadata server

You can use the Compute metadata server to fetch ID tokens with a specific audience as follows:

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 function you are invoking, such as https://GCP_REGION-PROJECT_ID.cloudfunctions.net/my-function.

Exchanging a self-signed JWT for a Google-signed ID token

  1. Assign the calling function's service account the Cloud Functions Invoker (roles/cloudfunctions.invoker) role.

  2. Add this service account as a member to the receiving function.

  3. Create a service account and a key and download the file with the private key (in JSON format) to the host from which the calling function or service makes its requests.

  4. Create a JWT with the header set to {"alg":"RS256","typ":"JWT"}. The payload should include a target_audience claim set to the URL of the receiving function and iss and sub claims set to the email address of the service account used above. It should also include exp, and iat claims. The aud claim should be set to https://www.googleapis.com/oauth2/v4/token.

  5. Use the private key downloaded above to sign the JWT.

  6. Using this JWT, send a POST request to https://www.googleapis.com/oauth2/v4/token. Authentication data must be included in the header and the body of the request.

    In the header:

    Authorization: Bearer $JWT - where $JWT is the JWT you just created
    Content-Type: application/x-www-form-urlencoded
    

    In the body:

    grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$JWT
    

    Replace $JWT with the JWT you just created

    This returns another JWT which includes an id_token signed by Google.

Send your GET/POST request to the receiving function. Include the Google-signed ID token in an Authorization: Bearer ID_TOKEN_JWT header in the request.