التحقّق من الطلبات من Google Chat

بالنسبة إلى تطبيقات Chat في Google Chat المستندة إلى نقاط نهاية HTTP، يوضّح هذا القسم كيفية التحقّق من أنّ الطلبات الموجّهة إلى نقطة النهاية واردة من Chat.

لإرسال أحداث التفاعل إلى نقطة النهاية الخاصة بتطبيق Chat، ترسل Google طلبات HTTPS إلى خدمتك. للتأكّد من أنّ الطلب وارد من Google، يضمِّن Chat رمز تعريف موقّعًا من Google بتنسيق OpenID Connect (OIDC) كـ رمز حامل مميز في عنوان Authorization لكل طلب HTTPS (وفي الحقل authorizationEventObject.systemIdToken من نص الطلب). على سبيل المثال:

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

السلسلة AbCdEf123456 في المثال السابق هي رمز مميز للترخيص. يتم توقيع هذا الرمز المشفّر باستخدام حساب الخدمة الفريد لكل مشروع في تطبيق Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com)، ويتم ضبط الحقل audience على عنوان URL لنقطة نهاية HTTP الذي تم إعداده لتطبيق Chat عند إعداد تطبيق Chat.

يمكنك نسخ عنوان البريد الإلكتروني لحساب الخدمة في تطبيق Chat من قسم إعدادات الربط في علامة التبويب الإعداد لواجهة برمجة التطبيقات Chat API في Google Cloud Console:

  1. في Google Cloud Console، انتقِل إلى القائمة > واجهات برمجة التطبيقات والخدمات > واجهات برمجة التطبيقات والخدمات المفعَّلة > Google Chat API > الإعدادات:

    الانتقال إلى إعدادات Google Chat API

  2. ضمن الميزات التفاعلية > إعدادات الربط، انسخ البريد الإلكتروني لحساب الخدمة (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

إذا كنت قد نفّذت تطبيق Chat باستخدام دوال Cloud Run أو Cloud Run، تتولّى خدمة Cloud IAM عملية التحقّق من الرمز المميّز تلقائيًا عند منح حساب خدمة تطبيق Chat دور Cloud Run Invoker (roles/run.invoker). إذا كان تطبيقك ينفِّذ خادم HTTP خاصًا به، يمكنك التحقّق من صحة رمز الحامل باستخدام مكتبة عميل Google API مفتوحة المصدر:

إذا لم يتم التحقّق من صحة الرمز المميّز لتطبيق Chat، يجب أن ترد خدمتك على الطلب باستخدام رمز استجابة HTTPS 401 (Unauthorized).

المصادقة على الطلبات باستخدام دوال Cloud Run

إذا تم تنفيذ منطق الدالة باستخدام وظائف Cloud Run أو Cloud Run، تأكَّد من أنّ عناوين URL لنقاط نهاية HTTP التي تم إعدادها ضمن المشغّلات في إعدادات الربط في تطبيق Chat تتوافق مع عنوان URL لنقطة نهاية دالة Cloud Run.

بعد ذلك، عليك منح إذن الوصول إلى حساب الخدمة الخاص بتطبيق Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com، الذي تم نسخه من قسم إعدادات الاتصال في علامة التبويب الإعدادات لواجهة برمجة التطبيقات Chat API) كمنفّذ باستخدام الخطوات التالية:

وحدة التحكّم

بعد نشر الدالة أو الخدمة على Google Cloud، اتّبِع الخطوات التالية:

  1. في Google Cloud Console، انتقِل إلى صفحة Cloud Run:

    الانتقال إلى Cloud Run

  2. في قائمة خدمات Cloud Run، انقر على مربّع الاختيار بجانب الدالة التي تتلقّى البيانات. (لا تنقر على الدالة نفسها).

  3. انقر على الأذونات في أعلى الشاشة. يتم فتح لوحة الأذونات.

  4. انقر على إضافة كيان أساسي.

  5. في حقل المستخدمون الرئيسيون الجدد، أدخِل عنوان البريد الإلكتروني لحساب الخدمة في تطبيق Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.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:service-PROJECT_NUMBER@gcp-sa-gsuiteaddons.iam.gserviceaccount.com'

غيِّر القيم في السلسلة على الشكل التالي:

  • RECEIVING_FUNCTION: اسم وظيفة تطبيق Chat.
  • استبدِل PROJECT_NUMBER برقم المشروع من عنوان البريد الإلكتروني لحساب الخدمة في تطبيق Chat.

المصادقة على طلبات HTTP باستخدام رمز تعريف

بالنسبة إلى نقاط نهاية HTTP، يكون رمز التفويض المميز في الطلب عبارة عن رمز تعريف بتوقيع Google من خلال معيار OpenID Connect ‏ (OIDC). يتم ضبط الحقل email على عنوان البريد الإلكتروني لحساب الخدمة في تطبيق Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com)، ويتم ضبط الحقل audience على عنوان URL لنقطة نهاية HTTP التي تم إعدادها لتلقّي الطلب. على سبيل المثال، إذا كانت نقطة النهاية التي تم ضبطها لتطبيق Chat هي https://example.com/app/، سيكون حقل audience في رمز التعريف https://example.com/app/.

هذه هي طريقة المصادقة المقترَحة إذا لم تكن نقطة نهاية HTTP مستضافة على خدمة تتيح المصادقة المستندة إلى إدارة الهوية وإمكانية الوصول (IAM) (مثل Cloud Run).

توضّح النماذج التالية كيفية التأكّد من أنّ رمز الدخول المميز قد أصدرته Google لتطبيق Chat واستهداف نقطة نهاية تطبيقك باستخدام مكتبة عميل Google OAuth:

جافا

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

تطبيقات Chat التي ليست إضافات: التحقّق من الطلبات الواردة من Google Chat

تنطبق المستندات التالية على تطبيقات Chat التي ليست إضافات Google Workspace. لنقل تطبيق Chat ليس إضافة، يُرجى الاطّلاع على تحويل تطبيق Google Chat إلى إضافة Google Workspace.

بالنسبة إلى تطبيقات Chat التي ليست إضافات تم ضبطها باستخدام عنوان URL لنقطة نهاية HTTP ضمن إعدادات الربط، يعتمد نوع رمز المصادقة المميزة وقيمة الحقل audience على نوع الجمهور المستهدف للمصادقة الذي اخترته عند ضبط تطبيق Chat، ويتم توقيع الطلبات بواسطة حساب الخدمة المشترك chat@system.gserviceaccount.com.

المصادقة على الطلبات باستخدام دوال Cloud Run (تطبيقات Chat التي ليست إضافات)

إذا تم تنفيذ منطق الدالة باستخدام دوال Cloud Run، عليك اختيار عنوان URL لنقطة نهاية HTTP في حقل الجمهور المستهدف للمصادقة ضمن إعدادات الربط في تطبيق Chat، والتأكّد من أنّ عنوان URL لنقطة نهاية HTTP في الإعدادات يتطابق مع عنوان URL لنقطة نهاية دالة Cloud Run.

بعد ذلك، عليك تفويض حساب خدمة Google Chat chat@system.gserviceaccount.com كجهة استدعاء باتّباع الخطوات التالية:

وحدة التحكّم

بعد نشر الدالة أو الخدمة على Google Cloud، اتّبِع الخطوات التالية:

  1. في Google Cloud Console، انتقِل إلى صفحة 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 التي ليست إضافات)

إذا تم ضبط حقل الجمهور المستهدف للمصادقة في إعدادات الربط لتطبيق Chat الذي ليس إضافة على عنوان URL لنقطة نهاية HTTP، سيكون رمز المصادقة المميز في الطلب رمز تعريف OpenID Connect (OIDC) موقّعًا من Google. تم ضبط الحقل email على chat@system.gserviceaccount.com. يتم ضبط حقل الجمهور المستهدف للمصادقة على عنوان URL الذي أعددت Google Chat لإرسال الطلبات إلى تطبيق Chat الذي ليس إضافة. على سبيل المثال، إذا كانت نقطة النهاية التي تم ضبطها لتطبيق Chat هي https://example.com/app/، سيكون حقل شريحة جمهور المصادقة في رمز التعريف https://example.com/app/.

توضّح النماذج التالية كيفية التأكّد من أنّ رمز الدخول المميز قد تم إصداره من خلال Google Chat وأنّه يستهدف تطبيق Chat الذي لا يمثّل إضافة باستخدام مكتبة عميل Google OAuth.

جافا

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 التي ليست إضافات)

إذا تم ضبط حقل الجمهور المستهدف للمصادقة في إعدادات الربط لتطبيق Chat الذي ليس إضافة على Project Number، سيكون رمز التفويض المميز في الطلب رمز JSON مميزًا للويب (JWT) موقّعًا ذاتيًا، صادرًا وموقّعًا من chat@system.gserviceaccount.com. يتم ضبط الحقل audience على رقم مشروع Google Cloud الذي استخدمته لإنشاء تطبيق Chat غير إضافي. على سبيل المثال، إذا كان رقم مشروع على السحابة الإلكترونية لتطبيق Chat هو 1234567890، سيكون الحقل audience في رمز JWT هو 1234567890.

توضّح النماذج التالية كيفية التحقّق من أنّ الرمز المميز لحامل التفويض صادر عن Google Chat وموجّه إلى مشروعك باستخدام مكتبة عميل Google OAuth.

جافا

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