Xác minh yêu cầu từ Google Chat

Đối với các ứng dụng Google Chat được xây dựng trên các điểm cuối HTTP, phần này giải thích cách xác minh rằng các yêu cầu đến điểm cuối của bạn là từ Chat.

Để gửi các sự kiện tương tác đến điểm cuối của ứng dụng Chat, Google sẽ gửi các yêu cầu HTTPS đến dịch vụ của bạn. Để xác minh rằng yêu cầu đến từ Google, Chat sẽ thêm một mã thông báo nhận dạng OpenID Connect (OIDC) do Google ký dưới dạng một mã thông báo truy cập trong tiêu đề Authorization của mọi yêu cầu HTTPS (và trong trường authorizationEventObject.systemIdToken của nội dung yêu cầu). Ví dụ:

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

Chuỗi AbCdEf123456 trong ví dụ trước là mã thông báo uỷ quyền của người mang. Mã thông báo mật mã này được ký bằng tài khoản dịch vụ duy nhất cho mỗi dự án của ứng dụng Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) và trường audience được đặt thành URL điểm cuối HTTP được định cấu hình cho ứng dụng Chat của bạn khi định cấu hình ứng dụng Chat.

Bạn có thể sao chép địa chỉ email tài khoản dịch vụ của ứng dụng Chat trong phần Chế độ cài đặt kết nối của thẻ Cấu hình Chat API trong Google Cloud Console:

  1. Trong Google Cloud Console, hãy chuyển đến Trình đơn > API và Dịch vụ > API và Dịch vụ đã bật > Google Chat API > Cấu hình:

    Chuyển đến phần Cấu hình Google Chat API

  2. Trong phần Tính năng tương tác > Cài đặt kết nối, hãy sao chép Địa chỉ email của tài khoản dịch vụ (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

Nếu bạn đã triển khai ứng dụng Chat bằng các hàm Cloud Run hoặc Cloud Run, thì Cloud IAM sẽ tự động xử lý quy trình xác minh mã thông báo khi bạn cấp cho tài khoản dịch vụ của ứng dụng Chat vai trò Cloud Run Invoker (roles/run.invoker). Nếu ứng dụng của bạn triển khai máy chủ HTTP riêng, bạn có thể xác minh mã thông báo truy cập bằng thư viện ứng dụng API của Google nguồn mở:

Nếu mã thông báo không xác minh được cho ứng dụng Chat, dịch vụ của bạn sẽ phản hồi yêu cầu bằng mã phản hồi HTTPS 401 (Unauthorized).

Xác thực các yêu cầu bằng cách sử dụng các hàm Cloud Run

Nếu bạn triển khai logic hàm bằng các hàm Cloud Run hoặc Cloud Run, hãy đảm bảo rằng các URL điểm cuối HTTP được định cấu hình trong mục Triggers (Trình kích hoạt) trong chế độ cài đặt kết nối của ứng dụng Chat tương ứng với URL của điểm cuối hàm Cloud Run.

Sau đó, hãy uỷ quyền cho tài khoản dịch vụ của ứng dụng Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, được sao chép từ phần Connection settings (Chế độ cài đặt kết nối) của thẻ Configuration (Cấu hình) trong Chat API) làm một trình gọi bằng cách thực hiện các bước sau:

Giao diện dòng lệnh

Sau khi triển khai hàm hoặc dịch vụ của bạn lên Google Cloud:

  1. Trong Cloud Console, hãy chuyển đến trang Cloud Run:

    Chuyển đến Cloud Run

  2. Trong danh sách dịch vụ Cloud Run, hãy nhấp vào hộp đánh dấu bên cạnh hàm nhận. (Đừng nhấp vào chính hàm đó.)

  3. Nhấp vào Quyền ở đầu màn hình. Ngăn Quyền sẽ mở ra.

  4. Nhấp vào Thêm người dùng chính.

  5. Trong trường Bên giao đại diện mới, hãy nhập địa chỉ email tài khoản dịch vụ của ứng dụng Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

  6. Trong trình đơn Chọn vai trò, hãy chọn vai trò Cloud Run

    Cloud Run Invoker.

  7. Nhấp vào Lưu.

gcloud

Sử dụng lệnh gcloud functions add-invoker-policy-binding:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:service-PROJECT_NUMBER@gcp-sa-gsuiteaddons.iam.gserviceaccount.com'

Thay thế nội dung sau:

  • RECEIVING_FUNCTION: tên của hàm ứng dụng Chat.
  • PROJECT_NUMBER: số dự án trong địa chỉ email tài khoản dịch vụ của ứng dụng Chat.

Xác thực các yêu cầu HTTP bằng mã nhận dạng

Đối với các điểm cuối HTTP, mã thông báo uỷ quyền của người mang trong yêu cầu là một mã thông báo nhận dạng OpenID Connect (OIDC) do Google ký. Trường email được đặt thành địa chỉ email tài khoản dịch vụ của ứng dụng Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) và trường audience được đặt thành URL điểm cuối HTTP được định cấu hình để nhận yêu cầu. Ví dụ: nếu điểm cuối đã định cấu hình của ứng dụng Chat là https://example.com/app/, thì trường audience trong mã thông báo nhận dạng là https://example.com/app/.

Đây là phương pháp xác thực được đề xuất nếu điểm cuối HTTP của bạn không được lưu trữ trên một dịch vụ hỗ trợ xác thực dựa trên IAM (chẳng hạn như Cloud Run).

Các mẫu sau đây cho thấy cách xác minh rằng mã thông báo truy cập đã được Google cấp cho ứng dụng Chat của bạn và nhắm đến điểm cuối của ứng dụng bằng cách sử dụng thư viện ứng dụng Google OAuth:

Java

java/chat/secured-app/src/main/java/com/google/chat/app/secured/App.java
/**
 * Determine whether a Google Workspace add-on request is legitimate.
 * 
 * @param event Event sent from Google Workspace add-on
 * @param authorization Authorization header from the request
 * @return {boolean} Whether the request is legitimate
 */
private boolean verifyAddOnRequest(JsonNode event, String authorization) throws Exception {
  JsonFactory factory = JacksonFactory.getDefaultInstance();

  GoogleIdTokenVerifier verifier =
    new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), factory)
      .setAudience(Collections.singletonList(HTTP_ENDPOINT))
      .build();

  String bearer = authorization.substring("Bearer ".length(), authorization.length());
  GoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);
  return idToken != null
    && verifier.verify(idToken)
    && idToken.getPayload().getEmailVerified()
    && idToken.getPayload().getEmail().equals(SERVICE_ACCOUNT_EMAIL);
}

Python

python/chat/secured-app/main.py
def verifyAddOnRequest() -> bool:
  """Determine whether a Google Workspace add-on request is legitimate.

  Args:
    request: Request sent from Google Workspace add-on

  Returns:
    Whether the request is legitimate
  """
  try:
    bearer = request.headers.get('Authorization')[len("Bearer "):]
    token = id_token.verify_oauth2_token(bearer, requests.Request(), HTTP_ENDPOINT)
    return token['email'] == SERVICE_ACCOUNT_EMAIL

  except:
    return False

Node.js

node/chat/secured-app/index.js
/**
 * Determine whether a Google Workspace add-on request is legitimate.
 * 
 * @param {Object} req Request sent from Google Workspace add-on
 * @return {boolean} Whether the request is legitimate
 */
async function verifyAddOnRequest(req) {
  try {
    const authorization = req.headers.authorization;
    const idToken = authorization.substring('Bearer '.length, authorization.length);
    const ticket = await new OAuth2Client().verifyIdToken({idToken, audience: HTTP_ENDPOINT});
    return ticket.getPayload().email_verified
        && ticket.getPayload().email === SERVICE_ACCOUNT_EMAIL;
  } catch (unused) {
    return false;
  }
}

Các ứng dụng trong Chat không phải là tiện ích bổ sung: Xác minh yêu cầu của Google Chat

Tài liệu sau đây áp dụng cho các ứng dụng Chat không phải là tiện ích bổ sung của Google Workspace. Để di chuyển một ứng dụng Chat không phải là tiện ích bổ sung, hãy xem bài viết Chuyển đổi ứng dụng Google Chat thành tiện ích bổ sung Google Workspace.

Đối với những ứng dụng Chat không phải là tiện ích bổ sung được định cấu hình bằng URL điểm cuối HTTP trong phần Chế độ cài đặt kết nối, loại mã thông báo truy cập và giá trị của trường audience phụ thuộc vào loại Đối tượng xác thực mà bạn đã chọn khi định cấu hình ứng dụng Chat, đồng thời các yêu cầu được ký bằng tài khoản dịch vụ dùng chung chat@system.gserviceaccount.com.

Xác thực các yêu cầu bằng hàm Cloud Run (Ứng dụng Chat không phải là tiện ích bổ sung)

Nếu triển khai logic hàm bằng các hàm Cloud Run, bạn phải chọn URL điểm cuối HTTP trong trường Đối tượng xác thực của chế độ cài đặt kết nối ứng dụng Chat và đảm bảo rằng URL điểm cuối HTTP trong cấu hình tương ứng với URL của điểm cuối hàm Cloud Run.

Sau đó, bạn cần uỷ quyền cho tài khoản dịch vụ Google Chat chat@system.gserviceaccount.com làm đối tượng gọi bằng cách làm theo các bước sau:

Giao diện dòng lệnh

Sau khi triển khai hàm hoặc dịch vụ của bạn lên Google Cloud:

  1. Trong Cloud Console, hãy chuyển đến trang Cloud Run:

    Chuyển đến Cloud Run

  2. Trong danh sách dịch vụ Cloud Run, hãy nhấp vào hộp đánh dấu bên cạnh hàm nhận. (Đừng nhấp vào chính hàm đó.)

  3. Nhấp vào Quyền ở đầu màn hình. Ngăn Quyền sẽ mở ra.

  4. Nhấp vào Thêm người dùng chính.

  5. Trong trường New principals (Chủ thể mới), hãy nhập chat@system.gserviceaccount.com.

  6. Trong trình đơn Chọn vai trò, hãy chọn vai trò Cloud Run

    Cloud Run Invoker.

  7. Nhấp vào Lưu.

gcloud

Sử dụng lệnh gcloud functions add-invoker-policy-binding:

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

Thay thế RECEIVING_FUNCTION bằng tên hàm của ứng dụng Chat.

Xác thực các yêu cầu HTTP bằng Mã nhận dạng (Ứng dụng trò chuyện không phải là tiện ích bổ sung)

Nếu trường Đối tượng xác thực của chế độ cài đặt kết nối Ứng dụng Chat không phải là một tiện ích bổ sung được đặt thành URL điểm cuối HTTP, thì mã thông báo uỷ quyền của người mang trong yêu cầu là mã thông báo nhận dạng OpenID Connect (OIDC) do Google ký. Trường email được đặt thành chat@system.gserviceaccount.com. Trường Đối tượng xác thực được đặt thành URL mà bạn đã định cấu hình Google Chat để gửi yêu cầu đến ứng dụng Chat không phải là tiện ích bổ sung. Ví dụ: nếu điểm cuối đã định cấu hình của ứng dụng trò chuyện là https://example.com/app/, thì trường Đối tượng xác thực trong mã thông báo nhận dạng là https://example.com/app/.

Các mẫu sau đây cho biết cách xác minh rằng mã thông báo truy cập của người mang được Google Chat phát hành và nhắm đến ứng dụng Chat của bạn không phải là một tiện ích bổ sung bằng cách sử dụng thư viện ứng dụng 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;
}

Xác thực các yêu cầu bằng JWT số dự án (Các ứng dụng Chat không phải là tiện ích bổ sung)

Nếu trường Đối tượng xác thực của chế độ cài đặt kết nối Ứng dụng Chat không phải là một tiện ích bổ sung được đặt thành Project Number, thì mã thông báo uỷ quyền của người mang trong yêu cầu là một JSON Web Token (JWT) tự ký, do chat@system.gserviceaccount.com phát hành và ký. Trường audience được đặt thành số dự án trên Google Cloud mà bạn đã dùng để tạo ứng dụng Chat không phải là tiện ích bổ sung. Ví dụ: nếu số dự án trên đám mây của ứng dụng Chat là 1234567890, thì trường audience trong JWT sẽ là 1234567890.

Các mẫu sau đây cho biết cách xác minh rằng mã thông báo truy cập được Google Chat phát hành và nhắm đến dự án của bạn bằng cách sử dụng thư viện Ứng dụng OAuth của Google.

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;
}