Проверка запросов от 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-сервер, вы можете проверить свой токен носителя с помощью клиентской библиотеки Google API с открытым исходным кодом:

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

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

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

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

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

Консоль

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

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

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

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

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

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

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

  6. В раскрывающемся меню «Выберите роль» выберите роль «Облачные функции» > «Вызов облачных функций» .

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

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

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

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

Ява

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.

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

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

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