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

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

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

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

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

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

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

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

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

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

Следующие шаги показывают, как использовать Cloud Functions (1-го поколения):

Консоль

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

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

    Перейти к облачным функциям

  2. В списке «Облачные функции» установите флажок рядом с функцией-получателем. (Не нажимайте на саму функцию.)

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

  4. Нажмите Добавить принципала .

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

  6. В раскрывающемся меню Выберите роль выберите роль Cloud Functions > Cloud Functions Invoker .

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

gcloud

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

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

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

Следующие шаги показывают, как использовать службы Cloud Functions (2-го поколения) или Cloud Run:

Консоль

После развертывания вашей функции или сервиса в 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-запросов с помощью ID-токена

Если поле «Аудитория аутентификации» в настройках подключения приложения Chat установлено на URL конечной точки HTTP , токен авторизации носителя в запросе будет представлять собой подписанный Google идентификатор OpenID Connect (OIDC). Поле email будет иметь значение chat@system.gserviceaccount.com . Поле «Аудитория аутентификации» будет иметь значение URL, который вы настроили в Google Chat для отправки запросов в приложение Chat. Например, если настроенная конечная точка приложения Chat — https://example.com/app/ , то поле «Аудитория аутентификации» в идентификаторе токена будет иметь значение https://example.com/app/ .

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

В следующих примерах показано, как проверить, что токен носителя был выдан Google Chat и предназначен для вашего приложения, используя клиентскую библиотеку Google OAuth.

Ява

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/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

узел/базовое-приложение/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

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

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

В следующих примерах показано, как проверить, что токен носителя был выпущен Google Chat и предназначен для вашего проекта, с использованием клиентской библиотеки Google OAuth.

Ява

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/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

узел/базовое-приложение/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;
}