Authentification pour l'appel

Pour créer, mettre à jour ou effectuer d'autres actions d'administration sur une fonction, vous devez disposer d'un compte principal doté d'un rôle approprié. Pour en savoir plus, consultez la section Autoriser l'accès via IAM.

Mais l'appel d'une fonction peut constituer un événement plus complexe. Les fonctions basées sur des événements ne peuvent être appelées que par la source de l'événement auquel elles sont abonnées. Toutefois, les fonctions HTTP peuvent être appelées par différents types d'identités, provenant de sources différentes. L'appelant peut être un développeur qui teste la fonction, ou bien une autre fonction ou un service qui souhaite utiliser la fonction. Par défaut, ces identités doivent s'authentifier (fournir une preuve de leur identité) et disposer des autorisations appropriées. L'accès sans authentification reste possible, mais il doit être activé. Pour en savoir plus, consultez la page Gérer les accès avec IAM.

Authentifier les tests des développeurs

En tant que développeur, vous avez besoin d'un accès pour créer, mettre à jour et supprimer des fonctions. Cet accès est accordé à l'aide du processus normal (IAM).

Toutefois, en tant que développeur, vous devrez aussi probablement appeler vos fonctions à des fins de test. Pour appeler une fonction à l'aide de curl ou d'outils similaires, vous devez :

  • avoir attribué au compte que vous utilisez pour accéder à Cloud Functions un rôle contenant l'autorisation cloudfunctions.functions.invoke. Par défaut, les rôles Administrateur Cloud Functions et Développeur Cloud Functions disposent de cette autorisation. Consultez la page Rôles IAM de Cloud Functions pour obtenir la liste complète des rôles et des autorisations associées.

  • (Si vous travaillez sur votre machine locale) avoir configuré l'accès via la ligne de commande en initialisant le SDK Google Cloud. Assurez-vous d'avoir téléchargé la clé de compte de service et d'avoir défini votre variable d'environnement GOOGLE_APPLICATION_CREDENTIALS.

  • Fournissez des identifiants d'authentification dans votre requête en tant que jeton d'ID généré par Google, stocké dans un en-tête Authorization. Par exemple, vous pouvez obtenir un jeton via gcloud comme suit :

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

Comme toujours, nous vous recommandons d'allouer l'ensemble minimal d'autorisations requises pour développer et utiliser vos fonctions. Assurez-vous que les stratégies IAM associées à vos fonctions sont limitées au nombre minimal d'utilisateurs et de comptes de service.

Authentifier les appels entre fonctions

Lorsque vous créez des services qui connectent plusieurs fonctions, il est recommandé de s'assurer que chaque fonction ne peut envoyer des requêtes qu'à un sous-ensemble spécifique de vos autres fonctions. Par exemple, si vous avez une fonction login, elle doit pouvoir accéder à la fonction user profiles, mais probablement pas à la fonction search.

Pour configurer la fonction de réception afin d'accepter les requêtes d'une fonction d'appel spécifique, vous devez attribuer le rôle "Demandeur Cloud Functions" (roles/cloudfunctions.invoker) au compte de service de la fonction d'appel sur la fonction de réception.

Console

  1. Accédez à Google Cloud Console :

    Accéder à Google Cloud Console

  2. Cochez la case en regard de la fonction de réception.

  3. Cliquez sur Autorisations en haut de l'écran. Le panneau Autorisations s'affiche.

  4. Cliquez sur Ajouter un compte principal.

  5. Dans le champ Nouveaux comptes principaux, saisissez l'identité de la fonction appelante. Il doit s'agir d'une adresse e-mail de compte de service.

  6. Sélectionnez le rôle Cloud Functions > Demandeur Cloud Functions dans le menu déroulant Sélectionner un rôle.

  7. Cliquez sur Enregistrer.

gcloud

Exécutez la commande gcloud functions add-iam-policy-binding :

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

RECEIVING_FUNCTION est le nom de la fonction de réception et CALLING_FUNCTION_IDENTITY est l'identité de la fonction d'appel, une adresse e-mail de compte de service.

Étant donné qu'elle va appeler la fonction de réception, la fonction appelante doit également fournir un jeton d'ID signé par Google pour s'authentifier. Il s'agit d'un processus en deux étapes :

  1. Créez un jeton d'ID signé par Google avec le champ d'audience (aud) défini sur l'URL de la fonction de réception.

  2. inclure le jeton d'ID dans un en-tête Authorization: Bearer ID_TOKEN de la requête destinée à la fonction.

Le moyen le plus simple et le plus fiable de gérer ce processus consiste à utiliser les bibliothèques d'authentification, comme indiqué ci-dessous, pour générer et utiliser ce jeton.

Générer des jetons de manière programmatique

Vous pouvez utiliser le code suivant pour générer un jeton d'ID. Ce code fonctionne dans tous les environnements dans lesquels les bibliothèques peuvent obtenir des identifiants d'authentification, y compris les environnements compatibles avec les identifiants par défaut de l'application locaux.

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: https://my-cloud-run-service.run.app/
// const targetAudience = 'https://TARGET_HOSTNAME/';
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);
  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();
  }
}

Générer des jetons manuellement

Si vous appelez une fonction et que, pour une raison quelconque, vous ne pouvez pas utiliser les bibliothèques d'authentification, vous pouvez obtenir le jeton d'ID manuellement de deux manières : soit à l'aide du serveur de métadonnées Compute, soit en créant un jeton JWT autosigné et en le remplaçant par un jeton d'ID signé par Google.

Utiliser le serveur de métadonnées

Vous pouvez utiliser le serveur de métadonnées Compute pour extraire des jetons d'identification associés à une audience spécifique, comme suit :

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

Où AUDIENCE correspond à l'URL de la fonction que vous appelez, telle que https://GCP_REGION-PROJECT_ID.cloudfunctions.net/my-function.

Remplacer un jeton JWT autosigné par un jeton d'ID signé par Google

  1. Accordez le rôle "Demandeur Cloud Functions" (roles/cloudfunctions.invoker) au compte de service de la fonction d'appel sur la fonction de réception.

  2. Créez un compte de service et une clé, puis téléchargez le fichier avec la clé privée (au format JSON) sur l'hôte à partir duquel la fonction appelante ou le service effectue ses requêtes.

  3. Créez un jeton JWT avec l'en-tête défini sur {"alg":"RS256","typ":"JWT"}. La charge utile doit inclure une revendication target_audience définie sur l'URL de la fonction de réception, ainsi que les revendications iss et sub définies sur l'adresse e-mail du compte de service utilisé ci-dessus. Elle doit également inclure des revendications exp et iat. La revendication aud doit être définie sur https://www.googleapis.com/oauth2/v4/token.

  4. Utilisez la clé privée téléchargée ci-dessus pour signer le jeton JWT.

  5. À l'aide de ce jeton JWT, envoyez une requête POST à https://www.googleapis.com/oauth2/v4/token. Les données d'authentification doivent être incluses dans l'en-tête et dans le corps de la requête.

    Dans l'en-tête :

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

    Dans le corps :

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

    Remplacez $JWT par le jeton JWT que vous venez de créer.

    Cette requête renvoie un autre jeton JWT, qui inclut un objet id_token signé par Google.

Envoyez votre requête GET/POST à la fonction de réception. Incluez le jeton d'ID signé par Google dans un en-tête Authorization: Bearer ID_TOKEN_JWT dans la requête.