Memverifikasi permintaan dari Google Chat

Untuk aplikasi Google Chat yang dibuat di endpoint HTTP, bagian ini menjelaskan cara memverifikasi bahwa permintaan ke endpoint Anda berasal dari Chat.

Untuk mengirimkan peristiwa interaksi ke endpoint aplikasi Chat Anda, Google membuat permintaan ke layanan Anda. Untuk memverifikasi bahwa permintaan berasal dari Google, Chat menyertakan token pembawa di header Authorization setiap permintaan HTTPS ke endpoint Anda. Contoh:

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

String AbCdEf123456 dalam contoh sebelumnya adalah token otorisasi pembawa. Ini adalah token kriptografi yang dibuat oleh Google. Jenis token pembawa dan nilai kolom audience bergantung pada jenis audiens autentikasi yang Anda pilih saat mengonfigurasi aplikasi Chat.

Jika Anda telah mengimplementasikan aplikasi Chat menggunakan fungsi Cloud Run, Cloud IAM akan menangani verifikasi token secara otomatis. Anda harus menambahkan akun layanan Google Chat sebagai pemanggil yang diotorisasi. Jika aplikasi Anda mengimplementasikan server HTTP-nya sendiri, Anda dapat memverifikasi token pembawa Anda menggunakan library klien Google API open source:

Jika token tidak diverifikasi untuk aplikasi Chat, layanan Anda harus merespons permintaan dengan kode respons HTTPS 401 (Unauthorized).

Mengautentikasi permintaan menggunakan fungsi Cloud Run

Jika logika fungsi Anda diimplementasikan menggunakan fungsi Cloud Run, Anda harus memilih HTTP endpoint URL di kolom Authentication Audience pada setelan koneksi aplikasi Chat dan memastikan bahwa HTTP endpoint URL dalam konfigurasi sesuai dengan URL endpoint fungsi Cloud Run.

Kemudian, Anda harus mengotorisasi akun layanan Google Chat chat@system.gserviceaccount.com sebagai pemanggil menggunakan langkah-langkah berikut:

Konsol

Setelah men-deploy fungsi atau layanan Anda ke Google Cloud:

  1. Di konsol Google Cloud, buka halaman Cloud Run:

    Buka Cloud Run

  2. Dalam daftar layanan Cloud Run, klik kotak centang di samping fungsi penerima. (Jangan klik fungsi itu sendiri.)

  3. Klik Permissions di bagian atas layar. Panel Permissions akan terbuka.

  4. Klik Add principal.

  5. Di kolom New principals, masukkan chat@system.gserviceaccount.com.

  6. Dari menu Select a role, pilih peran Cloud Run

    Cloud Run Invoker.

  7. Klik Save.

gcloud

Gunakan perintah gcloud functions add-invoker-policy-binding:

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

Ganti RECEIVING_FUNCTION dengan nama fungsi aplikasi Chat Anda.

Mengautentikasi permintaan HTTP dengan Token ID

Jika kolom Authentication Audience pada setelan koneksi aplikasi Chat ditetapkan ke , token otorisasi pembawa dalam permintaan adalah token ID OpenID Connect (OIDC) yang ditandatangani Google. Kolom email ditetapkan ke chat@system.gserviceaccount.com. Kolom Authentication Audience ditetapkan ke URL yang Anda konfigurasi agar Google Chat mengirim permintaan ke aplikasi Chat Anda. Misalnya, jika endpoint yang dikonfigurasi untuk aplikasi Chat Anda adalah https://example.com/app/, kolom Authentication Audience di token ID adalahhttps://example.com/app/.

Ini adalah metode autentikasi yang direkomendasikan jika endpoint HTTP Anda tidak dihosting di layanan yang mendukung autentikasi berbasis IAM (seperti Cloud Run). Dengan metode ini, layanan HTTP Anda memerlukan informasi tentang URL endpoint tempat layanan tersebut berjalan, tetapi tidak memerlukan informasi tentang nomor project Cloud.

Contoh berikut menunjukkan cara memverifikasi bahwa token pembawa dikeluarkan oleh Google Chat dan ditargetkan ke aplikasi Anda menggunakan library klien 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;
}

Mengautentikasi permintaan dengan JWT Nomor Project

Jika kolom Authentication Audience pada setelan koneksi aplikasi Chat ditetapkan ke Project Number, token otorisasi pembawa dalam permintaan adalah Token Web JSON (JWT) yang ditandatangani sendiri , yang dikeluarkan dan ditandatangani oleh chat@system.gserviceaccount.com. Kolom audience ditetapkan ke nomor project Google Cloud yang Anda gunakan untuk membuat aplikasi Chat. Misalnya, jika nomor project Cloud aplikasi Chat Anda adalah 1234567890, kolom audience di JWT adalah 1234567890.

Metode autentikasi ini hanya direkomendasikan jika Anda lebih memilih menggunakan nomor project Cloud untuk memverifikasi permintaan, bukan HTTP endpoint URL. Misalnya, jika Anda ingin mengubah endpoint URL dari waktu ke waktu sambil mempertahankan nomor project Cloud yang sama, atau jika Anda ingin menggunakan endpoint yang sama untuk beberapa nomor project Cloud dan ingin membandingkan kolom audience dengan daftar nomor project Cloud.

Contoh berikut menunjukkan cara memverifikasi bahwa token pembawa dikeluarkan oleh Google Chat dan ditargetkan ke project Anda menggunakan library klien 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;
}