Verificare le richieste da Google Chat

Per le app Google Chat create su endpoint HTTP, questa sezione spiega come verificare che le richieste al tuo endpoint provengano da Chat.

Per inviare eventi di interazione all'endpoint della tua app di chat, Google effettua richieste HTTPS al tuo servizio. Per verificare che la richiesta provenga da Google, Chat include un token ID OpenID Connect (OIDC) firmato da Google come token di connessione nell'intestazione Authorization di ogni richiesta HTTPS (e nel campo authorizationEventObject.systemIdToken del corpo della richiesta). Ad esempio:

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

La stringa AbCdEf123456 nell'esempio precedente è il token di autorizzazione bearer. Questo token crittografico è firmato dall'account di servizio univoco per progetto della tua app Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) e il campo audience è impostato sull'URL dell'endpoint HTTP configurato per la tua app Chat durante la configurazione dell'app Chat.

Puoi copiare l'indirizzo email del service account dell'app di chat dalla sezione Impostazioni di connessione della scheda Configurazione dell'API Chat nella console Google Cloud:

  1. Nella console Google Cloud, vai a Menu > API e servizi > API Google Chat > Configurazione:

    Vai alla configurazione dell'API Google Chat

  2. In Funzionalità interattive > Impostazioni di connessione, copia l'indirizzo email del service account (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

Se hai implementato la tua app Chat utilizzando Cloud Run Functions o Cloud Run, Cloud IAM gestisce automaticamente la verifica dei token quando concedi al service account dell'app Chat il ruolo Cloud Run Invoker (roles/run.invoker). Se la tua app implementa il proprio server HTTP, puoi verificare il token bearer utilizzando una libreria client delle API di Google open source:

Se il token non viene verificato per l'app di Chat, il tuo servizio deve rispondere alla richiesta con un codice di risposta HTTPS 401 (Unauthorized).

Autenticare le richieste utilizzando Cloud Run Functions

Se la logica della funzione è implementata utilizzando le funzioni Cloud Run o Cloud Run, assicurati che gli URL degli endpoint HTTP configurati in Trigger nelle impostazioni di connessione dell'app di chat corrispondano all'URL dell'endpoint della funzione Cloud Run.

Poi, autorizza il service account della tua app di chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, copiato dalla sezione Impostazioni di connessione della scheda Configurazione dell'API Chat) come chiamante seguendo questi passaggi:

Console

Dopo aver eseguito il deployment della funzione o del servizio su Google Cloud:

  1. Nella console Google Cloud, vai alla pagina Cloud Run:

    Vai a Cloud Run

  2. Nell'elenco dei servizi Cloud Run, fai clic sulla casella di controllo accanto alla funzione ricevente. Non fare clic sulla funzione stessa.

  3. Fai clic su Autorizzazioni nella parte superiore della schermata. Si apre il riquadro Autorizzazioni.

  4. Fai clic su Aggiungi entità.

  5. Nel campo Nuove entità, inserisci l'indirizzo email del service account dell'app di chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

  6. Nel menu Seleziona un ruolo, seleziona il ruolo Cloud Run.

    Cloud Run Invoker.

  7. Fai clic su Salva.

gcloud

Utilizza il comando gcloud functions add-invoker-policy-binding:

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

Sostituisci quanto segue:

  • RECEIVING_FUNCTION: il nome della funzione della tua app di chat.
  • PROJECT_NUMBER: il numero di progetto dell'indirizzo email del service account dell'app Chat.

Autenticare le richieste HTTP con un token ID

Per gli endpoint HTTP, il token di autorizzazione bearer nella richiesta è un token ID OpenID Connect (OIDC) firmato da Google. Il campo email è impostato sull'indirizzo email dell'account di servizio dell'app di chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com), mentre il campo audience è impostato sull'URL dell'endpoint HTTP configurato per ricevere la richiesta. Ad esempio, se l'endpoint configurato della tua app Chat è https://example.com/app/, il campo audience nel token ID è https://example.com/app/.

Questo è il metodo di autenticazione consigliato se l'endpoint HTTP non è ospitato su un servizio che supporta l'autenticazione basata su IAM (come Cloud Run).

I seguenti esempi mostrano come verificare che il token di tipo bearer sia stato emesso da Google per la tua app Chat e indirizzato all'endpoint della tua app utilizzando la libreria client Google OAuth:

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

App di chat che non sono componenti aggiuntivi: verifica le richieste di Google Chat

La seguente documentazione si applica alle app di chat che non sono componenti aggiuntivi di Google Workspace. Per eseguire la migrazione di un'app di Chat che non è un componente aggiuntivo, vedi Convertire un'app Google Chat in un componente aggiuntivo di Google Workspace.

Per le app Chat che non sono componenti aggiuntivi configurati con URL endpoint HTTP in Impostazioni di connessione, il tipo di token di connessione e il valore del campo audience dipendono dal tipo di Pubblico di autenticazione selezionato durante la configurazione dell'app Chat e le richieste sono firmate dal service account condiviso chat@system.gserviceaccount.com.

Autenticare le richieste utilizzando Cloud Run Functions (app di chat che non sono componenti aggiuntivi)

Se la logica della funzione è implementata utilizzando le funzioni Cloud Run, devi selezionare URL dell'endpoint HTTP nel campo Pubblico di autenticazione dell'impostazione di connessione dell'app Chat e assicurarti che l'URL dell'endpoint HTTP nella configurazione corrisponda all'URL dell'endpoint della funzione Cloud Run.

Dopodiché, devi autorizzare il service account Google Chat chat@system.gserviceaccount.com come chiamante seguendo questi passaggi:

Console

Dopo aver eseguito il deployment della funzione o del servizio su Google Cloud:

  1. Nella console Google Cloud, vai alla pagina Cloud Run:

    Vai a Cloud Run

  2. Nell'elenco dei servizi Cloud Run, fai clic sulla casella di controllo accanto alla funzione ricevente. Non fare clic sulla funzione stessa.

  3. Fai clic su Autorizzazioni nella parte superiore della schermata. Si apre il riquadro Autorizzazioni.

  4. Fai clic su Aggiungi entità.

  5. Nel campo Nuove entità, inserisci chat@system.gserviceaccount.com.

  6. Nel menu Seleziona un ruolo, seleziona il ruolo Cloud Run.

    Cloud Run Invoker.

  7. Fai clic su Salva.

gcloud

Utilizza il comando gcloud functions add-invoker-policy-binding:

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

Sostituisci RECEIVING_FUNCTION con il nome della funzione dell'app di chat.

Autenticare le richieste HTTP con un token ID (app di Chat che non sono componenti aggiuntivi)

Se il campo Authentication Audience dell'impostazione di connessione dell'app Chat che non è un componente aggiuntivo è impostato su URL endpoint HTTP, il token di autorizzazione bearer nella richiesta è un token ID OpenID Connect (OIDC) firmato da Google. Il campo email è impostato su chat@system.gserviceaccount.com. Il campo Pubblico di autenticazione è impostato sull'URL che hai configurato in Google Chat per inviare richieste alla tua app di Chat che non è un componente aggiuntivo. Ad esempio, se l'endpoint configurato della tua app di chat è https://example.com/app/, il campo Pubblico di autenticazione nel token ID è https://example.com/app/.

I seguenti esempi mostrano come verificare che il token di connessione sia stato emesso da Google Chat e indirizzato alla tua app di Chat che non è un componente aggiuntivo utilizzando la libreria client 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;
}

Autenticare le richieste con un JWT del numero di progetto (app Chat che non sono componenti aggiuntivi)

Se il campo Authentication Audience (Pubblico autenticazione) dell'impostazione di connessione dell'app di chat che non è un componente aggiuntivo è impostato su Project Number, il token di autorizzazione bearer nella richiesta è un token web JSON (JWT) autofirmato, emesso e firmato da chat@system.gserviceaccount.com. Il campo audience è impostato sul numero di progetto Google Cloud che hai utilizzato per creare l'app Chat che non è un componente aggiuntivo. Ad esempio, se il numero del progetto Cloud della tua app Chat è 1234567890, il campo audience nel JWT è 1234567890.

I seguenti esempi mostrano come verificare che il token bearer sia stato emesso da Google Chat e indirizzato al tuo progetto utilizzando la libreria client 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;
}