Проверка запросов от Google Chat

В этом разделе объясняется, как проверить, что запросы к вашей конечной точке поступают от приложения «Чат» (Chat) для приложений Google Chat, построенных на основе HTTP-запросов.

To dispatch interaction events to your Chat app's endpoint, Google makes requests to your service. To verify that the request is coming from Google, Chat includes a bearer token in the Authorization header of every HTTPS request to your endpoint. For example:

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

В приведенном выше примере строка AbCdEf123456 — это токен авторизации носителя. Это криптографический токен, созданный Google. Тип токена носителя и значение поля audience зависят от типа аудитории аутентификации, выбранного вами при настройке приложения «Чат» .

Если вы реализовали свое приложение чата с использованием функций Cloud Run, Cloud IAM автоматически обрабатывает проверку токена. Вам необходимо добавить учетную запись службы Google Chat в качестве авторизованного вызывающего пользователя. Если ваше приложение использует собственный HTTP-сервер, вы можете проверить свой токен носителя, используя клиентскую библиотеку Google API с открытым исходным кодом:

Если токен не проходит проверку в приложении чата, ваш сервис должен ответить на запрос кодом HTTPS 401 (Unauthorized) .

Аутентификация запросов с использованием функций Cloud Run.

Если логика вашей функции реализована с использованием функций Cloud Run, необходимо выбрать URL-адрес конечной точки HTTP в поле «Аудитория аутентификации» в настройках подключения приложения «Чат» и убедиться, что URL-адрес конечной точки HTTP в конфигурации соответствует URL-адресу конечной точки функции Cloud Run.

Затем необходимо авторизовать учетную запись службы Google Chat chat@system.gserviceaccount.com в качестве инициатора, выполнив следующие шаги:

Консоль

После развертывания вашей функции или сервиса в Google Cloud:

  1. В консоли Google Cloud перейдите на страницу Cloud Run:

    Перейдите в Cloud Run

  2. В списке служб Cloud Run установите флажок рядом с принимающей функцией. (Не устанавливайте флажок рядом с самой функцией.)

  3. Нажмите кнопку «Разрешения» в верхней части экрана. Откроется панель «Разрешения» .

  4. Нажмите «Добавить основного пользователя» .

  5. В поле «Новые участники» введите chat@system.gserviceaccount.com .

  6. В меню «Выберите роль» выберите роль «Cloud Run».

    Cloud Run Invoker .

  7. Нажмите « Сохранить ».

gcloud

Используйте команду gcloud functions add-invoker-policy-binding :

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

Замените RECEIVING_FUNCTION на название функции вашего чат-приложения.

Аутентификация HTTP-запросов с помощью токена идентификации.

If the Authentication Audience field of the Chat app connection setting is set to HTTP endpoint URL , the bearer authorization token in the request is a Google-signed OpenID Connect (OIDC) ID token . The email field is set to chat@system.gserviceaccount.com . The Authentication Audience field is set to the URL you configured Google Chat to send requests to your Chat app. For example, if the configured endpoint of your Chat app is https://example.com/app/ , then the Authentication Audience field in the ID token is https://example.com/app/ .

Это рекомендуемый метод аутентификации, если ваша HTTP-точка доступа не размещена в сервисе, поддерживающем аутентификацию на основе IAM (например, Cloud Run). При использовании этого метода вашему HTTP-сервису потребуется информация об URL-адресе точки доступа, на которой он запущен, но не потребуется информация о номере проекта Cloud.

В следующих примерах показано, как проверить, что токен bearer был выдан Google Chat и предназначен для вашего приложения, используя клиентскую библиотеку 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;
}

Аутентификация запросов с помощью номера проекта JWT

Если в поле «Аудитория аутентификации» в настройках подключения приложения чата установлено значение Project Number , то токен авторизации носителя в запросе представляет собой самоподписанный JSON Web Token (JWT) , выданный и подписанный chat@system.gserviceaccount.com . Поле audience устанавливается на номер проекта Google Cloud, который вы использовали при создании своего приложения чата. Например, если номер проекта Cloud вашего приложения чата — 1234567890 , то поле audience в JWT будет иметь значение 1234567890 .

Этот метод аутентификации рекомендуется использовать только в том случае, если вы предпочитаете использовать номер проекта Cloud для проверки запросов вместо URL-адреса конечной точки HTTP. Например, если вы хотите со временем изменить URL-адрес конечной точки, сохраняя при этом тот же номер проекта Cloud, или если вы хотите использовать одну и ту же конечную точку для нескольких номеров проектов Cloud и хотите сравнить поле audience " со списком номеров проектов Cloud.

В следующих примерах показано, как проверить, что токен bearer был выдан Google Chat и предназначен для вашего проекта, используя клиентскую библиотеку 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;
}