Weryfikowanie próśb z Google Chat

W przypadku aplikacji Google Chat opartych na punktach końcowych HTTP w tej sekcji dowiesz się, jak sprawdzić, czy żądania wysyłane do punktu końcowego pochodzą z Google Chat.

Aby wysyłać zdarzenia interakcji do punktu końcowego aplikacji Google Chat, Google wysyła do Twojej usługi żądania HTTPS. Aby sprawdzić, czy żądanie pochodzi od Google, Chat dołącza do każdego żądania HTTPS w nagłówku Authorization (i w polu authorizationEventObject.systemIdToken treści żądania) podpisany przez Google token identyfikacyjny OpenID Connect (OIDC) jako token okaziciela. Przykład:

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

Ciąg AbCdEf123456 w poprzednim przykładzie to token autoryzacji typu bearer. Ten token kryptograficzny jest podpisany przez unikalne konto usługi aplikacji do Google Chat w projekcie (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com), a pole audience jest ustawione na adres URL punktu końcowego HTTP skonfigurowany dla aplikacji do Google Chat podczas konfigurowania aplikacji do Google Chat.

Adres e-mail konta usługi aplikacji do obsługi czatu możesz skopiować z sekcji Ustawienia połączenia na karcie Konfiguracja interfejsu Chat API w konsoli Google Cloud:

  1. W konsoli Google Cloud otwórz Menu > Interfejsy API i usługi > Włączone interfejsy API i usługi > Google Chat API > Konfiguracja:

    Otwórz konfigurację interfejsu Google Chat API

  2. W sekcji Funkcje interaktywne > Ustawienia połączenia skopiuj adres e-mail konta usługi (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

Jeśli aplikacja do obsługi czatu została wdrożona za pomocą funkcji Cloud Run lub Cloud Run, Cloud IAM automatycznie obsługuje weryfikację tokena, gdy przyznasz konto usługi aplikacji do obsługi czatu rolę Cloud Run Invoker (roles/run.invoker). Jeśli Twoja aplikacja implementuje własny serwer HTTP, możesz zweryfikować token okaziciela za pomocą biblioteki klienta interfejsów API Google o otwartym kodzie źródłowym:

Jeśli token nie zostanie zweryfikowany w aplikacji Google Chat, usługa powinna odpowiedzieć na żądanie kodem odpowiedzi HTTPS 401 (Unauthorized).

Uwierzytelnianie żądań za pomocą funkcji Cloud Run

Jeśli logika funkcji jest zaimplementowana za pomocą funkcji Cloud Run lub Cloud Run, sprawdź, czy adresy URL punktów końcowych HTTP skonfigurowane w sekcji Triggery w ustawieniach połączenia aplikacji do obsługi czatu odpowiadają adresowi URL punktu końcowego funkcji Cloud Run.

Następnie autoryzuj konto usługi aplikacji Google Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, skopiowane z sekcji Ustawienia połączenia na karcie Konfiguracja interfejsu Chat API) jako wywołującego, wykonując te czynności:

Konsola

Po wdrożeniu funkcji lub usługi w Google Cloud:

  1. W konsoli Google Cloud otwórz stronę Cloud Run:

    Otwórz Cloud Run

  2. Na liście usług Cloud Run kliknij pole wyboru obok funkcji odbierającej. (Nie klikaj samej funkcji).

  3. U góry ekranu kliknij Uprawnienia. Otworzy się panel Uprawnienia.

  4. Kliknij Dodaj podmiot zabezpieczeń.

  5. W polu Nowe podmioty zabezpieczeń wpisz adres e-mail konta usługi aplikacji do obsługi czatu (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

  6. W menu Wybierz rolę wybierz rolę Cloud Run.

    Wywołujący Cloud Run.

  7. Kliknij Zapisz.

gcloud

Użyj polecenia 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'

Zastąp te elementy:

  • RECEIVING_FUNCTION: nazwa funkcji aplikacji do Google Chat.
  • PROJECT_NUMBER: numer projektu z adresu e-mail konta usługi aplikacji Google Chat.

Uwierzytelnianie żądań HTTP za pomocą tokena identyfikatora

W przypadku punktów końcowych HTTP token autoryzacji w żądaniu jest podpisanym przez Google tokenem identyfikacji ID OpenID Connect (OIDC). Pole email jest ustawione na adres e-mail konta usługi aplikacji do Google Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com), a pole audience jest ustawione na adres URL punktu końcowego HTTP skonfigurowany do odbierania żądania. Jeśli na przykład skonfigurowany punkt końcowy aplikacji Google Chat to https://example.com/app/, pole audience w tokenie identyfikatora ma wartość https://example.com/app/.

Jest to zalecana metoda uwierzytelniania, jeśli punkt końcowy HTTP nie jest hostowany w usłudze obsługującej uwierzytelnianie oparte na IAM (np. Cloud Run).

Poniższe przykłady pokazują, jak za pomocą biblioteki klienta Google OAuth sprawdzić, czy token okaziciela został wydany przez Google dla Twojej aplikacji do obsługi czatu i czy jest przeznaczony dla punktu końcowego aplikacji:

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

Aplikacje do obsługi czatu, które nie są dodatkami: weryfikowanie próśb z Google Chat

Poniższa dokumentacja dotyczy aplikacji do Google Chat, które nie są dodatkami do Google Workspace. Aby przeprowadzić migrację aplikacji do czatu, która nie jest dodatkiem, przeczytaj artykuł Przekształcanie aplikacji do Google Chat w dodatek do Google Workspace.

W przypadku aplikacji do Google Chat, które nie są dodatkami skonfigurowanymi za pomocą adresu URL punktu końcowego HTTP w sekcji Ustawienia połączenia, typ tokena okaziciela i wartość pola audience zależą od typu odbiorców uwierzytelniania wybranego podczas konfigurowania aplikacji do Google Chat, a żądania są podpisywane przez wspólne konto usługi chat@system.gserviceaccount.com.

Uwierzytelnianie żądań za pomocą funkcji Cloud Run (aplikacje do obsługi czatu, które nie są dodatkami)

Jeśli logika funkcji jest zaimplementowana za pomocą funkcji Cloud Run, w polu Odbiorca uwierzytelniania ustawienia połączenia aplikacji Google Chat musisz wybrać Adres URL punktu końcowego HTTP i upewnić się, że adres URL punktu końcowego HTTP w konfiguracji odpowiada adresowi URL punktu końcowego funkcji Cloud Run.

Następnie musisz autoryzować konto usługi Google Chatchat@system.gserviceaccount.com jako wywołującego, wykonując te czynności:

Konsola

Po wdrożeniu funkcji lub usługi w Google Cloud:

  1. W konsoli Google Cloud otwórz stronę Cloud Run:

    Otwórz Cloud Run

  2. Na liście usług Cloud Run kliknij pole wyboru obok funkcji odbierającej. (Nie klikaj samej funkcji).

  3. U góry ekranu kliknij Uprawnienia. Otworzy się panel Uprawnienia.

  4. Kliknij Dodaj podmiot zabezpieczeń.

  5. W polu Nowe podmioty zabezpieczeń wpisz chat@system.gserviceaccount.com.

  6. W menu Wybierz rolę wybierz rolę Cloud Run.

    Wywołujący Cloud Run.

  7. Kliknij Zapisz.

gcloud

Użyj polecenia gcloud functions add-invoker-policy-binding:

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

Zastąp RECEIVING_FUNCTION nazwą funkcji aplikacji do obsługi czatu w Google Chat.

Uwierzytelnianie żądań HTTP za pomocą tokena identyfikatora (aplikacje do obsługi czatu, które nie są dodatkami)

Jeśli w polu Authentication Audience (Odbiorcy uwierzytelniania) ustawienia połączenia aplikacji do czatu, która nie jest dodatkiem, jest ustawiona wartość HTTP endpoint URL (Adres URL punktu końcowego HTTP), token autoryzacji typu bearer w żądaniu jest podpisanym przez Google tokenem tożsamości OpenID Connect (OIDC) ID token. Pole email ma wartość chat@system.gserviceaccount.com. Pole Authentication Audience (Odbiorcy uwierzytelniania) jest ustawione na adres URL, który został skonfigurowany w Google Chat do wysyłania żądań do aplikacji Google Chat, która nie jest dodatkiem. Jeśli na przykład skonfigurowany punkt końcowy aplikacji Google Chat to https://example.com/app/, pole Authentication Audience w tokenie identyfikatora ma wartość https://example.com/app/.

Poniższe przykłady pokazują, jak za pomocą biblioteki klienta Google OAuth sprawdzić, czy token okaziciela został wydany przez Google Chat i jest przeznaczony dla aplikacji do obsługi czatu, która nie jest dodatkiem.

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

Uwierzytelnianie żądań za pomocą tokena JWT z numerem projektu (aplikacje do obsługi czatu, które nie są dodatkami)

Jeśli pole Authentication Audience (Odbiorcy uwierzytelniania) w ustawieniach połączenia aplikacji do czatu, która nie jest dodatkiem, ma wartość Project Number, token autoryzacji typu bearer w żądaniu jest samodzielnie podpisany tokenem internetowym JSON (JWT), wydanym i podpisanym przez chat@system.gserviceaccount.com. Pole audience jest ustawione na numer projektu Google Cloud, którego użyto do utworzenia aplikacji Google Chat, która nie jest dodatkiem. Jeśli na przykład numer projektu Cloud aplikacji do czatu to 1234567890, pole audience w tokenie JWT ma wartość 1234567890.

Te przykłady pokazują, jak za pomocą biblioteki klienta Google OAuth sprawdzić, czy token okaziciela został wydany przez Google Chat i jest przeznaczony dla Twojego projektu.

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