ยืนยันคำขอจาก Google Chat

สำหรับแอป Google Chat ที่สร้างขึ้นบนปลายทาง HTTP ส่วนนี้จะอธิบายวิธี ยืนยันว่าคำขอไปยังปลายทางมาจาก Chat

Google จะส่งคำขอไปยังบริการของคุณเพื่อส่งเหตุการณ์การโต้ตอบไปยังปลายทางของแอป Chat Chat จะใส่โทเค็นผู้ถือในส่วนหัว Authorization ของคำขอ HTTPS ทุกรายการไปยังปลายทางของคุณเพื่อยืนยันว่าคำขอมาจาก Google เช่น

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

สตริง AbCdEf123456 ในตัวอย่างก่อนหน้าคือโทเค็นการให้สิทธิ์แบบ Bearer นี่คือโทเค็นการเข้ารหัสที่ Google สร้างขึ้น ประเภทของโทเค็น ผู้ถือและค่าของฟิลด์ audience จะขึ้นอยู่กับประเภทของกลุ่มเป้าหมายการตรวจสอบสิทธิ์ที่คุณเลือกเมื่อกําหนดค่าแอป Chat

หากคุณได้ติดตั้งใช้งานแอป Chat โดยใช้ Cloud Functions หรือ Cloud Run แล้ว Cloud IAM จะจัดการการยืนยันโทเค็นโดยอัตโนมัติ คุณเพียงต้องเพิ่มบัญชีบริการของ Google Chat เป็นผู้เรียกใช้ที่ได้รับอนุญาต หากแอปของคุณใช้เซิร์ฟเวอร์ HTTP ของตัวเอง คุณสามารถยืนยันโทเค็น Bearer ได้ โดยใช้ไลบรารีของไคลเอ็นต์ Google API แบบโอเพนซอร์ส

หากโทเค็นไม่ได้รับการยืนยันสำหรับแอป Chat บริการของคุณควรตอบกลับคำขอด้วยรหัสการตอบกลับ HTTPS 401 (Unauthorized)

ตรวจสอบสิทธิ์คำขอโดยใช้ Cloud Functions หรือ Cloud Run

หากใช้ตรรกะของฟังก์ชันโดยใช้ Cloud Functions หรือ Cloud Run คุณต้องเลือก URL ของปลายทาง HTTP ในช่องกลุ่มเป้าหมายการตรวจสอบสิทธิ์ของ การตั้งค่าการเชื่อมต่อแอป Chat และตรวจสอบว่า URL ของปลายทาง HTTP ในการกำหนดค่าตรงกับ URL ของปลายทาง 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

ตรวจสอบสิทธิ์คำขอ HTTP ด้วยโทเค็นรหัส

หากตั้งค่าฟิลด์กลุ่มเป้าหมายการตรวจสอบสิทธิ์ของการตั้งค่าการเชื่อมต่อแอป Chat เป็น URL ปลายทาง HTTP โทเค็นการให้สิทธิ์แบบ Bearer ในคำขอจะเป็น OpenID Connect (OIDC) ที่ Google ลงนามโทเค็นรหัส ตั้งค่าฟิลด์ 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 ของปลายทางที่บริการทำงานอยู่ แต่ไม่จำเป็นต้องมีข้อมูลเกี่ยวกับ หมายเลขโปรเจ็กต์ Cloud

ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่าโทเค็นผู้ถือสิทธิ์ออกโดย 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 เป็น Project Number โทเค็นการให้สิทธิ์แบบ Bearer ในคำขอจะเป็นโทเค็นเว็บ JSON (JWT) ที่ลงนามด้วยตนเอง ซึ่งออกและลงนามโดย chat@system.gserviceaccount.com ฟิลด์ audience จะตั้งค่าเป็นหมายเลขโปรเจ็กต์ Google Cloud ที่คุณใช้ สร้างแอป Chat ตัวอย่างเช่น หากหมายเลขโปรเจ็กต์ Cloud ของแอป Chat คือ 1234567890 ฟิลด์ audience ใน JWT จะเป็น 1234567890

เราขอแนะนำให้ใช้วิธีการตรวจสอบสิทธิ์นี้เฉพาะในกรณีที่คุณต้องการใช้ หมายเลขโปรเจ็กต์ Cloud เพื่อยืนยันคำขอแทน URL ของปลายทาง HTTP เช่น หากต้องการเปลี่ยน URL ของปลายทางเมื่อเวลาผ่านไปขณะที่ ยังคงใช้หมายเลขโปรเจ็กต์ Cloud เดิม หรือหากต้องการใช้ปลายทางเดียวกัน สำหรับหมายเลขโปรเจ็กต์ Cloud หลายหมายเลขและต้องการเปรียบเทียบฟิลด์ audience กับรายการหมายเลขโปรเจ็กต์ Cloud

ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่าโทเค็นผู้ถือได้รับการออกโดย 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;
}