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

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

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

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

  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 จาก เมนูแบบเลื่อนลงเลือกบทบาท

  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

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

หากช่องกลุ่มเป้าหมายในการตรวจสอบสิทธิ์ของแอป Chat ตั้งค่าการตั้งค่าการเชื่อมต่อเป็น App URL แล้ว โทเค็นการให้สิทธิ์สำหรับผู้ถือในคำขอเป็น OpenID Connect ที่ Google รับรอง (OIDC) โทเค็นรหัส ตั้งค่าช่อง email เป็น chat@system.gserviceaccount.com ช่อง audience ได้รับการตั้งค่าเป็น URL ที่คุณกำหนดค่าให้ Google Chat ส่ง คำขอไปยังแอป Chat ของคุณ ตัวอย่างเช่น หาก ปลายทางที่กำหนดค่าไว้ของแอป Chat คือ https://example.com/app/ ช่อง audience ในโทเค็นรหัสจะเป็น https://example.com/app/

ตัวอย่างต่อไปนี้แสดงวิธียืนยันว่าโทเค็นสำหรับผู้ถือออกโดย Google Chat และกำหนดเป้าหมายไปยังแอปโดยใช้ไลบรารีของไคลเอ็นต์ OAuth ของ 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();

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

ตัวอย่างต่อไปนี้แสดงวิธียืนยันว่าโทเค็นสำหรับผู้ถือออกโดย Google Chat และกำหนดเป้าหมายที่โปรเจ็กต์โดยใช้ไลบรารีของไคลเอ็นต์ OAuth ของ 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;
}