验证来自 Google Chat 的请求

对于基于 HTTP 端点构建的 Google Chat 应用,本部分介绍了如何 验证向端点发送请求是否来自 Chat。

要将互动事件分派给 Chat 应用的 端点,Google 就会向您的服务发出请求。验证请求 来自 Google,Chat 包括一个 不记名令牌Authorization例如:

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

上述示例中的字符串 AbCdEf123456 是不记名授权 令牌。这是 Google 生成的加密令牌。不记名类型 词元和 audience 字段取决于您在设置时选择的身份验证受众群体类型。 配置 Chat 应用

如果您是使用 Cloud 部署的 Chat 应用, Functions 或 Cloud Run,Cloud IAM 会自动处理令牌验证。您 只需将 Google Chat 服务账号添加为已获授权的调用方即可。 如果您的应用实现了自己的 HTTP 服务器,您可以验证自己的不记名令牌 使用开源 Google API 客户端库

如果系统没有针对 Chat 应用验证令牌,您的 服务应该使用 HTTPS 响应代码来响应请求 401 (Unauthorized)

使用 Cloud Functions 或 Cloud Run 对请求进行身份验证

如果您的函数逻辑是使用 Cloud Functions 或 Cloud Run 实现的,您需要 必须在 Firebase SDK 的 Authentication Audience(身份验证受众)字段中选择 App URL Chat 应用 连接设置,并确保 配置中的应用网址与 Cloud Functions 函数的网址对应,或者 Cloud Run 端点。

然后,您需要为该 Google Chat 服务账号授权 chat@system.gserviceaccount.com 作为调用方。

以下步骤展示了如何使用 Cloud Functions (第 1 代):

控制台

将函数部署到 Google Cloud 后:

  1. 在 Google Cloud 控制台中,转到 Cloud Functions 页面:

    转到 Cloud Functions

  2. 在 Cloud Functions 列表中,点击接收消息旁边的复选框 函数。(请勿点击函数本身。)

  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 替换为您的 Chat 应用的功能。

以下步骤显示了如何使用 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 替换为您的 Chat 应用的功能。

使用应用网址 ID 令牌对请求进行身份验证

如果 Chat 应用的“Authentication Audience”(身份验证对象)字段 连接设置设为 App URL, 请求中的不记名授权令牌是由 Google 签名的 OpenID Connect (OIDC) ID 令牌email 字段设置为 chat@system.gserviceaccount.comaudience 字段已设为您配置 Google Chat 发送的网址 向 Chat 应用发送消息。例如,如果 您 Chat 应用的已配置端点 https://example.com/app/,则 ID 令牌中的 audience 字段为 https://example.com/app/

以下示例展示了如何验证不记名令牌是否由 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 对请求进行身份验证

如果 Chat 应用的“Authentication Audience”(身份验证对象)字段 连接设置设置为 Project Number,则请求中的不记名授权令牌是自签名 JSON 网络令牌 (JWT), 由chat@system.gserviceaccount.com签发并签署。 audience 字段设置为您使用的 Google Cloud 项目编号 来构建 Chat 应用。例如,如果您的 Chat 应用的 Cloud 项目编号为 1234567890,则 JWT 中的 audience 字段为 1234567890

以下示例展示了如何验证不记名令牌是否由 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;
}