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

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

Для отправки событий взаимодействия на конечную точку вашего чат-приложения Google отправляет запросы к вашему сервису. Чтобы убедиться, что запрос исходит от Google, чат включает токен носителя в заголовок Authorization каждого HTTPS-запроса к вашей конечной точке. Например:

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-запросов с помощью токена идентификации.

Если в поле «Аудитория аутентификации» в настройках подключения приложения чата установлено значение «URL конечной точки HTTP» , то токен авторизации носителя в запросе представляет собой подписанный Google токен OpenID Connect (OIDC). В поле email установлено значение chat@system.gserviceaccount.com . В поле « Аудитория аутентификации» устанавливается URL-адрес, который вы настроили для отправки запросов в ваше приложение чата через Google Chat. Например, если настроенная конечная точка вашего приложения чата — https://example.com/app/ , то поле « Аудитория аутентификации» в токене ID будет иметь значение 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;
}