Verifica las solicitudes de Google Chat

En el caso de las apps de Google Chat creadas en extremos HTTP, en esta sección, se explica cómo verificar que las solicitudes a tu extremo provengan de Chat.

Para enviar eventos de interacción al extremo de tu app de Chat, Google realiza solicitudes a tu servicio. Para verificar que la solicitud provenga de Google, Chat incluye un token de portador en el encabezado Authorization de cada solicitud HTTPS a tu extremo. Por ejemplo:

POST
Host: yourappurl.com
Authorization: Bearer AbCdEf123456
Content-Type: application/json
User-Agent: Google-Dynamite

La cadena AbCdEf123456 en el ejemplo anterior es el token de autorización de portador. Este es un token criptográfico producido por Google. El tipo de token de portador y el valor del campo audience dependen del tipo de público de autenticación que seleccionaste cuando configuraste la app de Chat.

Si implementaste tu app de Chat con Cloud Functions o Cloud Run, Cloud IAM controla la verificación de tokens automáticamente. Solo debes agregar la cuenta de servicio de Google Chat como invocador autorizado. Si tu app implementa su propio servidor HTTP, puedes verificar tu token de portador con una biblioteca cliente de la API de Google de código abierto:

Si el token no se verifica para la app de Chat, tu servicio debe responder a la solicitud con un código de respuesta HTTPS 401 (Unauthorized).

Autentica solicitudes con Cloud Functions o Cloud Run

Si la lógica de tu función se implementa con Cloud Functions o Cloud Run, debes seleccionar URL del extremo HTTP en el campo Público de autenticación del parámetro de configuración de conexión de la app de Chat y asegurarte de que la URL del extremo HTTP en la configuración corresponda a la URL del extremo de Cloud Functions o Cloud Run.

Luego, debes autorizar la cuenta de servicio de Google Chat chat@system.gserviceaccount.com como invocador.

En los siguientes pasos, se muestra cómo usar Cloud Functions (1ª gen.):

Console

Después de implementar tu función en Google Cloud, haz lo siguiente:

  1. En la consola de Google Cloud, ve a la página Cloud Functions.

    Ir a Cloud Functions

  2. En la lista de Cloud Functions, 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 chat@system.gserviceaccount.com.

  6. Selecciona el rol Cloud Functions > Cloud Functions Invoker en el menú desplegable Selecciona 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:chat@system.gserviceaccount.com' \
  --role='roles/cloudfunctions.invoker'

Reemplaza RECEIVING_FUNCTION por el nombre de la función de tu app de Chat.

En los siguientes pasos, se muestra cómo usar los servicios de Cloud Functions (2ª gen.) o Cloud Run:

Console

Después de implementar tu función o servicio en Google Cloud, haz lo siguiente:

  1. En la consola de Google Cloud, ve a la página Cloud Run:

    Ir a Cloud Run

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

  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 chat@system.gserviceaccount.com.

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

  7. Haz clic en Guardar.

gcloud

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

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:chat@system.gserviceaccount.com'

Reemplaza RECEIVING_FUNCTION por el nombre de la función de tu app de Chat.

Autentica solicitudes HTTP con un token de ID

Si el campo Público de autenticación del parámetro de configuración de conexión de la app de Chat está establecido en URL del extremo HTTP, el token de autorización de portador en la solicitud es un token de ID de OpenID Connect (OIDC) firmado por Google. El campo email está configurado como chat@system.gserviceaccount.com. El campo Público de autenticación se establece en la URL en la que configuraste Google Chat para que envíe solicitudes a tu app de Chat. Por ejemplo, si el extremo configurado de tu app de Chat es https://example.com/app/, el campo Público de autenticación en el token de ID es https://example.com/app/.

Este es el método de autenticación recomendado si tu extremo HTTP no está alojado en un servicio que admite la autenticación basada en IAM (como Cloud Functions o Cloud Run). Con este método, tu servicio HTTP necesita información sobre la URL del extremo en el que se ejecuta, pero no necesita información sobre el número del proyecto de Cloud.

En los siguientes ejemplos, se muestra cómo verificar que Google Chat emitió el token de portador y que está dirigido a tu app con la biblioteca cliente de Google OAuth.

Java

java/basic-app/src/main/java/com/google/chat/app/basic/App.java
String CHAT_ISSUER = "chat@system.gserviceaccount.com";
JsonFactory factory = JacksonFactory.getDefaultInstance();

GoogleIdTokenVerifier verifier =
    new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), factory)
        .setAudience(Collections.singletonList(AUDIENCE))
        .build();

GoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);
return idToken != null
    && verifier.verify(idToken)
    && idToken.getPayload().getEmailVerified()
    && idToken.getPayload().getEmail().equals(CHAT_ISSUER);

Python

python/basic-app/main.py
# Bearer Tokens received by apps will always specify this issuer.
CHAT_ISSUER = 'chat@system.gserviceaccount.com'

try:
    # Verify valid token, signed by CHAT_ISSUER, intended for a third party.
    request = requests.Request()
    token = id_token.verify_oauth2_token(bearer, request, AUDIENCE)
    return token['email'] == CHAT_ISSUER

except:
    return False

Node.js

node/basic-app/index.js
// Bearer Tokens received by apps will always specify this issuer.
const chatIssuer = 'chat@system.gserviceaccount.com';

// Verify valid token, signed by chatIssuer, intended for a third party.
try {
  const ticket = await client.verifyIdToken({
    idToken: bearer,
    audience: audience
  });
  return ticket.getPayload().email_verified
      && ticket.getPayload().email === chatIssuer;
} catch (unused) {
  return false;
}

Autentica solicitudes con un JWT de número de proyecto

Si el campo Authentication Audience del parámetro de configuración de conexión de la app de Chat está establecido en Project Number, el token de autorización de portador en la solicitud es un token web JSON (JWT) autofirmado, emitido y firmado por chat@system.gserviceaccount.com. El campo audience se establece en el número de proyecto de Google Cloud que usaste para compilar tu app de Chat. Por ejemplo, si el número de proyecto de Cloud de tu app de Chat es 1234567890, el campo audience en el JWT será 1234567890.

Este método de autenticación solo se recomienda si prefieres usar el número de proyecto de Cloud para verificar las solicitudes en lugar de la URL del extremo HTTP. Por ejemplo, si deseas cambiar la URL del extremo con el tiempo y mantener el mismo número de proyecto de Cloud, o si deseas usar el mismo extremo para varios números de proyecto de Cloud y comparar el campo audience con una lista de números de proyecto de Cloud.

En los siguientes ejemplos, se muestra cómo verificar que Google Chat emitió el token de portador y que está dirigido a tu proyecto con la biblioteca cliente de Google OAuth.

Java

java/basic-app/src/main/java/com/google/chat/app/basic/App.java
String CHAT_ISSUER = "chat@system.gserviceaccount.com";
JsonFactory factory = JacksonFactory.getDefaultInstance();

GooglePublicKeysManager keyManagerBuilder =
    new GooglePublicKeysManager.Builder(new ApacheHttpTransport(), factory)
        .setPublicCertsEncodedUrl(
            "https://www.googleapis.com/service_accounts/v1/metadata/x509/" + CHAT_ISSUER)
        .build();

GoogleIdTokenVerifier verifier =
    new GoogleIdTokenVerifier.Builder(keyManagerBuilder).setIssuer(CHAT_ISSUER).build();

GoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);
return idToken != null
    && verifier.verify(idToken)
    && idToken.verifyAudience(Collections.singletonList(AUDIENCE))
    && idToken.verifyIssuer(CHAT_ISSUER);

Python

python/basic-app/main.py
# Bearer Tokens received by apps will always specify this issuer.
CHAT_ISSUER = 'chat@system.gserviceaccount.com'

try:
    # Verify valid token, signed by CHAT_ISSUER, intended for a third party.
    request = requests.Request()
    certs_url = 'https://www.googleapis.com/service_accounts/v1/metadata/x509/' + CHAT_ISSUER
    token = id_token.verify_token(bearer, request, AUDIENCE, certs_url)
    return token['iss'] == CHAT_ISSUER

except:
    return False

Node.js

node/basic-app/index.js
// Bearer Tokens received by apps will always specify this issuer.
const chatIssuer = 'chat@system.gserviceaccount.com';

// Verify valid token, signed by CHAT_ISSUER, intended for a third party.
try {
  const response = await fetch('https://www.googleapis.com/service_accounts/v1/metadata/x509/' + chatIssuer);
  const certs = await response.json();
  await client.verifySignedJwtWithCertsAsync(
    bearer, certs, audience, [chatIssuer]);
  return true;
} catch (unused) {
  return false;
}