Anfragen von Google Chat prüfen

In diesem Abschnitt wird beschrieben, wie Sie bei Google Chat-Apps, die auf HTTP-Endpunkten basieren, überprüfen können, ob die Anfragen an Ihren Endpunkt von Chat stammen.

Um Interaktionsereignisse an den Endpunkt Ihrer Chat-App zu senden, führt Google HTTPS-Anfragen an Ihren Dienst aus. Um zu überprüfen, ob die Anfrage von Google stammt, enthält Chat ein von Google signiertes OIDC-ID-Token (OpenID Connect) als Inhabertoken im Authorization-Header jeder HTTPS-Anfrage (und im Feld authorizationEventObject.systemIdToken des Anfragetexts). Beispiel:

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

Der String AbCdEf123456 im vorherigen Beispiel ist das Bearer-Autorisierungstoken. Dieses kryptografische Token wird von dem eindeutigen, projektbezogenen Dienstkonto (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) Ihrer Chat-App signiert. Das Feld audience wird auf die HTTP-Endpunkt-URL gesetzt, die für Ihre Chat-App beim Konfigurieren der Chat-App konfiguriert wurde.

Sie können die E-Mail-Adresse des Dienstkontos Ihrer Chat-App in der Google Cloud Console im Abschnitt Verbindungseinstellungen auf dem Tab Konfiguration der Chat API kopieren:

  1. Rufen Sie in der Google Cloud Console das Menü > APIs und Dienste > Aktivierte APIs und Dienste > Google Chat API > Konfiguration auf:

    Zur Google Chat API-Konfiguration

  2. Kopieren Sie unter Interaktive Funktionen > Verbindungseinstellungen die E-Mail-Adresse des Dienstkontos (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

Wenn Sie Ihre Chat-App mit Cloud Run Functions oder Cloud Run implementiert haben, übernimmt Cloud IAM die Tokenüberprüfung automatisch, wenn Sie dem Dienstkonto der Chat-App die Rolle Cloud Run Invoker (roles/run.invoker) zuweisen. Wenn Ihre App einen eigenen HTTP-Server implementiert, können Sie das Inhabertoken mit einer Open-Source-Google API-Clientbibliothek überprüfen:

Wenn das Token für die Chat-App nicht bestätigt wird, sollte Ihr Dienst mit dem HTTPS-Antwortcode 401 (Unauthorized) auf die Anfrage antworten.

Anfragen mit Cloud Run Functions authentifizieren

Wenn Ihre Funktionslogik mit Cloud Run Functions oder Cloud Run implementiert wird, achten Sie darauf, dass die HTTP-Endpunkt-URLs, die unter Triggers (Trigger) in den Verbindungseinstellungen der Chat-App konfiguriert sind, der URL Ihres Cloud Run-Funktionsendpunkts entsprechen.

Autorisieren Sie dann das Dienstkonto Ihrer Chat-App (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, kopiert aus dem Bereich Verbindungseinstellungen auf dem Tab Konfiguration der Chat API) als Aufrufer. Gehen Sie dazu so vor:

Console

Nachdem Sie Ihre Funktion oder Ihren Dienst in Google Cloud bereitgestellt haben:

  1. Wechseln Sie in der Google Cloud Console zur Seite Cloud Run.

    Zu Cloud Run

  2. Klicken Sie in der Liste der Cloud Run-Dienste auf das Kästchen neben der empfangenden Funktion. (Klicken Sie nicht auf die Funktion selbst.)

  3. Klicken Sie oben auf dem Bildschirm auf Berechtigungen. Der Bereich Berechtigungen wird geöffnet.

  4. Klicken Sie auf Hauptkonto hinzufügen.

  5. Geben Sie im Feld Neue Hauptkonten die E-Mail-Adresse des Dienstkontos Ihrer Chat-App (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) ein.

  6. Wählen Sie im Menü Rolle auswählen die Rolle Cloud Run aus.

    Cloud Run-Aufrufer.

  7. Klicken Sie auf Speichern.

gcloud

Führen Sie den Befehl gcloud functions add-invoker-policy-binding aus:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:service-PROJECT_NUMBER@gcp-sa-gsuiteaddons.iam.gserviceaccount.com'

Ersetzen Sie Folgendes:

  • RECEIVING_FUNCTION: Der Name der Funktion Ihrer Chat-App.
  • PROJECT_NUMBER: Die Projektnummer aus der E-Mail-Adresse des Dienstkontos Ihrer Chat-App.

HTTP-Anfragen mit einem ID-Token authentifizieren

Für HTTP-Endpunkte ist das Bearer-Autorisierungstoken in der Anfrage ein von Google signiertes OIDC-ID-Token (OpenID Connect). Das Feld email ist auf die E-Mail-Adresse des Dienstkontos Ihrer Chat-App (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) und das Feld audience auf die HTTP-Endpunkt-URL festgelegt, die für den Empfang der Anfrage konfiguriert ist. Wenn der konfigurierte Endpunkt Ihrer Chat-App beispielsweise https://example.com/app/ ist, ist das Feld audience im ID-Token https://example.com/app/.

Dies ist die empfohlene Authentifizierungsmethode, wenn Ihr HTTP-Endpunkt nicht auf einem Dienst gehostet wird, der die IAM-basierte Authentifizierung unterstützt (z. B. Cloud Run).

Die folgenden Beispiele zeigen, wie Sie mit der Google OAuth-Clientbibliothek überprüfen, ob das Inhabertoken von Google für Ihre Chat-App ausgestellt wurde und auf den Endpunkt Ihrer App ausgerichtet ist:

Java

java/chat/secured-app/src/main/java/com/google/chat/app/secured/App.java
/**
 * Determine whether a Google Workspace add-on request is legitimate.
 * 
 * @param event Event sent from Google Workspace add-on
 * @param authorization Authorization header from the request
 * @return {boolean} Whether the request is legitimate
 */
private boolean verifyAddOnRequest(JsonNode event, String authorization) throws Exception {
  JsonFactory factory = JacksonFactory.getDefaultInstance();

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

  String bearer = authorization.substring("Bearer ".length(), authorization.length());
  GoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);
  return idToken != null
    && verifier.verify(idToken)
    && idToken.getPayload().getEmailVerified()
    && idToken.getPayload().getEmail().equals(SERVICE_ACCOUNT_EMAIL);
}

Python

python/chat/secured-app/main.py
def verifyAddOnRequest() -> bool:
  """Determine whether a Google Workspace add-on request is legitimate.

  Args:
    request: Request sent from Google Workspace add-on

  Returns:
    Whether the request is legitimate
  """
  try:
    bearer = request.headers.get('Authorization')[len("Bearer "):]
    token = id_token.verify_oauth2_token(bearer, requests.Request(), HTTP_ENDPOINT)
    return token['email'] == SERVICE_ACCOUNT_EMAIL

  except:
    return False

Node.js

node/chat/secured-app/index.js
/**
 * Determine whether a Google Workspace add-on request is legitimate.
 * 
 * @param {Object} req Request sent from Google Workspace add-on
 * @return {boolean} Whether the request is legitimate
 */
async function verifyAddOnRequest(req) {
  try {
    const authorization = req.headers.authorization;
    const idToken = authorization.substring('Bearer '.length, authorization.length);
    const ticket = await new OAuth2Client().verifyIdToken({idToken, audience: HTTP_ENDPOINT});
    return ticket.getPayload().email_verified
        && ticket.getPayload().email === SERVICE_ACCOUNT_EMAIL;
  } catch (unused) {
    return false;
  }
}

Chat-Apps, die keine Add-ons sind: Anfragen aus Google Chat bestätigen

Die folgende Dokumentation gilt für Chat-Apps, die keine Google Workspace-Add‑ons sind. Informationen zum Migrieren einer Google Chat-App, die kein Add‑on ist, finden Sie unter Google Chat-App in ein Google Workspace-Add‑on umwandeln.

Bei Chat-Apps, die keine Add-ons sind und mit HTTP-Endpunkt-URL unter Verbindungseinstellungen konfiguriert wurden, hängen der Typ des Inhabertokens und der Wert des Felds audience vom Typ der Authentifizierungszielgruppe ab, die Sie beim Konfigurieren der Chat-App ausgewählt haben. Anfragen werden vom freigegebenen Dienstkonto chat@system.gserviceaccount.com signiert.

Anfragen mit Cloud Run Functions authentifizieren (Chat-Apps, die keine Add-ons sind)

Wenn Ihre Funktionslogik mit Cloud Run-Funktionen implementiert wird, müssen Sie im Feld Authentifizierungszielgruppe der Verbindungseinstellung der Chat-App die Option HTTP-Endpunkt-URL auswählen. Außerdem muss die HTTP-Endpunkt-URL in der Konfiguration der URL des Cloud Run-Funktionsendpunkts entsprechen.

Anschließend müssen Sie das Google Chat-Dienstkonto chat@system.gserviceaccount.com als Aufrufer autorisieren. Gehen Sie dazu so vor:

Console

Nachdem Sie Ihre Funktion oder Ihren Dienst in Google Cloud bereitgestellt haben:

  1. Wechseln Sie in der Google Cloud Console zur Seite Cloud Run.

    Zu Cloud Run

  2. Klicken Sie in der Liste der Cloud Run-Dienste auf das Kästchen neben der empfangenden Funktion. (Klicken Sie nicht auf die Funktion selbst.)

  3. Klicken Sie oben auf dem Bildschirm auf Berechtigungen. Der Bereich Berechtigungen wird geöffnet.

  4. Klicken Sie auf Hauptkonto hinzufügen.

  5. Geben Sie im Feld Neue Hauptkonten chat@system.gserviceaccount.com ein.

  6. Wählen Sie im Menü Rolle auswählen die Rolle Cloud Run aus.

    Cloud Run-Aufrufer.

  7. Klicken Sie auf Speichern.

gcloud

Führen Sie den Befehl gcloud functions add-invoker-policy-binding aus:

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

Ersetzen Sie RECEIVING_FUNCTION durch den Namen der Funktion Ihrer Chat-App.

HTTP-Anfragen mit einem ID-Token authentifizieren (Chat-Apps, die keine Add-ons sind)

Wenn das Feld Authentication Audience (Authentifizierungszielgruppe) der Verbindungseinstellung für die Chat-App, die kein Add-on ist, auf HTTP endpoint URL (HTTP-Endpunkt-URL) festgelegt ist, ist das Bearer-Autorisierungstoken in der Anfrage ein von Google signiertes OIDC-ID-Token (OpenID Connect). Das Feld email ist auf chat@system.gserviceaccount.com gesetzt. Das Feld Authentication Audience (Authentifizierungszielgruppe) ist auf die URL festgelegt, die Sie für Google Chat konfiguriert haben, um Anfragen an Ihre Chat-App zu senden, die kein Add-on ist. Wenn der konfigurierte Endpunkt Ihrer Chat-App beispielsweise https://example.com/app/ ist, lautet das Feld Authentication Audience im ID-Token https://example.com/app/.

In den folgenden Beispielen wird gezeigt, wie Sie mit der Google OAuth-Clientbibliothek prüfen, ob das Inhabertoken von Google Chat ausgestellt wurde und auf Ihre Chat-App ausgerichtet ist, die kein Add-on ist.

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;
}

Anfragen mit einem JWT mit Projektnummer authentifizieren (Chat-Apps, die keine Add-ons sind)

Wenn das Feld Authentication Audience (Authentifizierungszielgruppe) der Verbindungseinstellung der Chat-App, die kein Add-on ist, auf Project Number festgelegt ist, ist das Bearer-Autorisierungstoken in der Anfrage ein selbst signiertes JSON Web Token (JWT), das von chat@system.gserviceaccount.com ausgestellt und signiert wurde. Das Feld audience ist auf die Google Cloud-Projektnummer festgelegt, die Sie zum Erstellen Ihrer Chat-App verwendet haben, die kein Add‑on ist. Wenn die Cloud-Projektnummer Ihrer Chat-App beispielsweise 1234567890 ist, lautet das Feld audience im JWT 1234567890.

Die folgenden Beispiele zeigen, wie Sie mit der Google OAuth-Clientbibliothek überprüfen, ob das Inhabertoken von Google Chat ausgestellt wurde und auf Ihr Projekt ausgerichtet ist.

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;
}