Autentica para la invocación

Para invocar una función autenticada de Cloud Functions, la principal subyacente debe tener el permiso de IAM del invocador.

Si deseas obtener permiso para crear, actualizar o realizar otras acciones administrativas en una función, debes tener una principal que tenga un rol adecuado. Para obtener más información, consulta Usa IAM para autorizar el acceso.

Sin embargo, invocar una función puede ser un evento más complejo. Solo la fuente del evento a la que están suscritas las funciones controladas por eventos puede invocarlas, pero las funciones de HTTP pueden ser invocadas por de diferentes tipos de identidades, originadas en diferentes lugares. El invocador puede ser un desarrollador que está probando la función o puede ser otra función o servicio que desee usarla. De forma predeterminada, estas identidades deben proporcionar un token de ID con la solicitud para autenticarse. Además, la cuenta que se usa también debe tener los permisos adecuados.

El acceso no autenticado sin un token de ID es posible, pero debe habilitarse. Para obtener más información, consulta Usa IAM para autorizar el acceso.

Autentica las pruebas de los desarrolladores

Como desarrollador, necesitas acceso para crear, actualizar y borrar funciones, y este se otorga mediante el proceso normal (de IAM).

Sin embargo, como desarrollador, es posible que debas invocar las funciones para realizar pruebas. Para invocar una función mediante curl o herramientas similares, ten en cuenta lo siguiente:

  • Asigna un rol a tu cuenta de usuario de Cloud Functions que contenga el permiso para invocar.

    • cloudfunctions.functions.invoke permiso cloudfunctions.functions.invoke para las funciones de 1ª gen. Por lo general, esto se hace a través del rol de invocador de Cloud Functions. De manera predeterminada, los roles de Administrador y Desarrollador de Cloud Functions tienen este permiso. Consulta los roles de IAM de Cloud Functions para ver la lista completa de los roles y los permisos asociados a estos.
    • run.routes.invoke para las funciones de 2ª gen. Por lo general, esto se hace a través del rol de invocador de Cloud Run.
  • Si trabajas desde tu máquina local, configura el acceso a la línea de comandos. Para ello, inicializa Google Cloud CLI.

  • Proporciona tu solicitud con credenciales de autenticación como un token de ID generado por Google almacenado en un encabezado Authorization. Por ejemplo, obtén un token de ID con gcloud mediante la ejecución del siguiente comando:

    curl  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
      https://FUNCTION_URL
    

    donde FUNCTION_URL es la URL de tu función. Recupera esta URL desde la página de Cloud Functions de la consola de Google Cloud o ejecuta el comando gcloud functions describe como se muestra en el primer paso del ejemplo de comando de implementación de Google Cloud CLI.

Puedes usar tokens creados por gcloud para invocar funciones de HTTP en cualquier proyecto, siempre y cuando tu cuenta tenga el permiso cloudfunctions.functions.invoke en la función que se invoca. Para fines de desarrollo, usa tokens de ID generados por gcloud. Sin embargo, ten en cuenta que estos tokens no tienen una reclamación de público, lo que los hace susceptibles a los ataques de retransmisión. En entornos de producción, usa tokens de ID emitidos para una cuenta de servicio con el público adecuado especificado. Este enfoque mejora la seguridad gracias a la restricción del uso del token solo al servicio previsto.

Como siempre, te recomendamos asignar el conjunto mínimo de permisos necesarios para desarrollar y usar tus funciones. Asegúrate de que las políticas de IAM en tus funciones estén limitadas a la cantidad mínima de usuarios y cuentas de servicio.

Autentica tu función para realizar llamadas

Cuando compilas servicios que conectan varias funciones, es una buena idea asegurarte de que cada función solo pueda enviar solicitudes a un subconjunto específico de tus otras funciones. Por ejemplo, si tienes una función login, esta debería poder acceder a la función user profiles, pero probablemente no debería poder acceder a la función search.

Si deseas configurar la función receptora para que acepte solicitudes de una función de llamada específica, debes otorgar el rol de invocador a la cuenta de servicio de la función que llama en la función receptora. Para las funciones de 1ª gen., el rol de invocador es Invocador de Cloud Functions (roles/cloudfunctions.invoker). Para las funciones de 2ª gen., el rol de invocador es Invocador de Cloud Run (roles/run.invoker) y se deben otorgar en el servicio subyacente.

Cloud Functions (1st gen):

Consola

Para las funciones de 1a gen., usa el invocador de Cloud Functions:

  1. Ve a Google Cloud Console:

    Ir a la consola de Google Cloud

  2. Haz clic en la casilla de verificación junto a la función receptora. (No hagas clic en la función).

  3. Haz clic en Permisos en la parte superior de la pantalla. Se abrirá el panel Permisos.

  4. Haz clic en Agregar principal.

  5. En el campo Principales nuevas, ingresa la identidad de la función que realiza la llamada. Debería ser el correo electrónico de una cuenta de servicio.

  6. Selecciona Cloud Functions > Invocador de Cloud Functions en el menú desplegable Seleccionar un rol.

  7. Haz clic en Guardar.

gcloud

Usa el comando gcloud functions add-iam-policy-binding:

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

En el que RECEIVING_FUNCTION es el nombre de la función receptora y CALLING_FUNCTION_IDENTITY es la identidad de la función que llama, un correo electrónico de cuenta de servicio.

Cloud Functions (2nd gen):

Consola

Para las funciones de 2a gen., usa el invocador de Cloud Run:

  1. Ve a Google Cloud Console:

    Ir a la consola de Google Cloud

  2. En la lista de servicios de Cloud Run, haz clic en la casilla de verificación junto a la función receptora. (No hagas clic en la función).

    Se abrirá el panel Permisos.

  3. Haz clic en Agregar principal.

  4. Ingresa la identidad del servicio emisor. Por lo general, es una dirección de correo electrónico predeterminada PROJECT_NUMBER-compute@developer.gserviceaccount.com.

    Ten en cuenta que el número del proyecto difiere del ID y el nombre del mismo. Puedes buscar el número del proyecto en la página del panel de la consola de Google Cloud.

  5. Elige la función Cloud Run Invoker Selecciona un rol del menú desplegable.

  6. Haz clic en Guardar.

gcloud

Usa el comando gcloud functions add-invoker-policy-binding:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY'

En el que RECEIVING_FUNCTION es el nombre de la función receptora y CALLING_FUNCTION_IDENTITY es la identidad de la función que llama, un correo electrónico de cuenta de servicio.

Debido a que invocará la función receptora, la función que realiza la llamada también debe proporcionar un token de ID firmado por Google para autenticarse. Este es un proceso en dos pasos:

  1. Crea un token de ID firmado por Google con el campo de público (aud) configurado en la URL de la función receptora.

  2. Incluye el token de ID en un encabezado Authorization: Bearer ID_TOKEN en la solicitud a la función.

La forma más fácil y confiable de administrar este proceso es usar las bibliotecas de autenticación como se muestra a continuación para generar y emplear este token.

Genera tokens de manera programática

Después de que el siguiente código genere un token de ID, llama a la función de Cloud Functions con ese token en tu nombre. Este código funciona en cualquier entorno en el que las bibliotecas puedan obtener credenciales de autenticación, incluidos los que admitan credenciales predeterminadas de la aplicación locales.

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */

// Cloud Functions uses your function's url as the `targetAudience` value
// const targetAudience = 'https://project-region-projectid.cloudfunctions.net/myFunction';
// For Cloud Functions, endpoint (`url`) and `targetAudience` should be equal
// const url = targetAudience;

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.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(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 Functions uses your function's URL as the `audience` value
    # audience = https://project-region-projectid.cloudfunctions.net/myFunction
    # For Cloud Functions, `endpoint` and `audience` should be equal

    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()

Comienza a usarlo


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 {
	// For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
	// Example `audience` value (Cloud Functions): https://<PROJECT>-<REGION>-<PROJECT_ID>.cloudfunctions.net/myFunction
	// (`targetURL` and `audience` will differ for 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`.
  //
  // For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
  // Example `audience` value (Cloud Functions): https://project-region-projectid.cloudfunctions.net/myFunction
  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();
  }
}

Genera tokens de forma manual

Si invocas una función y, por algún motivo, no puedes usar las bibliotecas de autenticación, hay dos maneras de obtener el token de ID manualmente, ya sea mediante el Servidor de metadatos de Compute o mediante la creación de un JWT autofirmado y intercambiarlo por un token de ID firmado por Google.

Usa el servidor de metadatos

Puedes usar el servidor de metadatos de Compute para obtener tokens de identidad con un público específico de la siguiente manera:

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

Reemplaza AUDIENCE por la URL de la función que invocas. Puedes recuperar esta URL como se describe en la sección anterior Autentica las pruebas de desarrolladores.

Intercambia un JWT autofirmado por un token de ID firmado por Google

  1. Otorga el rol de Invocador de Cloud Functions (roles/cloudfunctions.invoker) a la cuenta de servicio de la función que llama en la función receptora.

  2. Crea una cuenta de servicio y una clave y descarga el archivo con la clave privada (en formato JSON) en el host desde el que la función o el servicio que realiza la llamada realiza sus solicitudes.

  3. Crea un JWT con el encabezado configurado como {"alg":"RS256","typ":"JWT"}. La carga útil debe incluir una reclamación target_audience configurada para la URL de la función receptora y las reclamaciones iss y sub configuradas para la dirección de correo electrónico de la cuenta de servicio que se usó antes. También debe incluir las reclamaciones exp y iat. La reclamación aud se debe configurar como https://www.googleapis.com/oauth2/v4/token.

  4. Usa la clave privada que descargaste antes para firmar el JWT.

  5. Mediante el uso de este JWT, envía una solicitud POST a https://www.googleapis.com/oauth2/v4/token. Los datos de autenticación deben incluirse en el encabezado y el cuerpo de la solicitud.

    En el encabezado:

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

    En el cuerpo, haz lo siguiente:

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

    Reemplaza $JWT por el JWT que acabas de crear.

    Esto muestra otro JWT que incluye un id_token firmado por Google.

Envía tu solicitud GET/POST a la función receptora. Incluye el token de ID firmado por Google en un encabezado Authorization: Bearer ID_TOKEN_JWT de la solicitud.

¿Qué sigue?