אימות בקשות מ-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 Functions או Cloud Run, אימות האסימונים יתבצע באופן אוטומטי על ידי Cloud IAM. פשוט צריך להוסיף את חשבון השירות של Google Chat כגורם מפעיל מורשה. אם האפליקציה מטמיעה שרת HTTP משלה, אפשר לאמת את אסימון בעל החזקה באמצעות ספריית לקוח של Google API בקוד פתוח:

אם האסימון לא מאומת באפליקציית Chat, השירות שלכם צריך להשיב לבקשה עם קוד תגובה מסוג HTTPS‏ 401 (Unauthorized).

אימות בקשות באמצעות Cloud Functions או Cloud Run

אם לוגיק הפונקציה מיושם באמצעות Cloud Functions או Cloud Run, צריך לבחור באפשרות כתובת URL של נקודת קצה מסוג HTTP בשדה Authentication Audience בהגדרת החיבור של אפליקציית Chat, ולוודא שכתובת ה-URL של נקודת הקצה מסוג HTTP בתצורה תואמת לכתובת ה-URL של נקודת הקצה ב-Cloud Functions או ב-Cloud Run.

לאחר מכן, צריך לאשר את חשבון השירות של Google Chat‏ chat@system.gserviceaccount.com כגורם מפעיל.

השלבים הבאים מראים איך להשתמש ב-Cloud Functions (דור ראשון):

המסוף

אחרי שפרסתם את הפונקציה ב-Google Cloud:

  1. נכנסים לדף Cloud Functions במסוף Google Cloud:

    כניסה לדף Cloud Functions

  2. ברשימה של Cloud Functions, לוחצים על תיבת הסימון לצד הפונקציה המקבלת. (לא לוחצים על הפונקציה עצמה).

  3. לוחצים על Permissions (הרשאות) בחלק העליון של המסך. נפתחת החלונית Permissions.

  4. לוחצים על Add principal.

  5. בשדה New principals, מזינים chat@system.gserviceaccount.com.

  6. בתפריט הנפתח Select a role, בוחרים את התפקיד 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 (דור שני) או ב-Cloud Run:

המסוף

אחרי שפרסתם את הפונקציה או השירות ב-Google Cloud:

  1. נכנסים לדף Cloud Run במסוף Google Cloud:

    כניסה אל Cloud Run

  2. ברשימת השירותים של Cloud Run, לוחצים על תיבת הסימון לצד הפונקציה המקבלת. (לא לוחצים על הפונקציה עצמה).

  3. לוחצים על Permissions (הרשאות) בחלק העליון של המסך. נפתחת החלונית Permissions.

  4. לוחצים על Add principal.

  5. בשדה New principals, מזינים chat@system.gserviceaccount.com.

  6. בתפריט הנפתח Select a role, בוחרים את התפקיד 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 באמצעות אסימון מזהה

אם השדה Authentication Audience (קהל האימות) בהגדרת החיבור של אפליקציית Chat מוגדר כ-HTTP endpoint URL (כתובת ה-URL של נקודת הקצה ל-HTTP), אסימון ההרשאה למוכ"ז בבקשה הוא אסימון מזהה של OpenID Connect ‏(OIDC) בחתימת Google. השדה email מוגדר ל-chat@system.gserviceaccount.com. השדה Authentication Audience מוגדר לכתובת ה-URL שהגדרתם ב-Google Chat לשליחת בקשות לאפליקציית Chat. לדוגמה, אם נקודת הקצה שמוגדרת באפליקציית Chat היא https://example.com/app/, השדה Authentication Audience באסימון המזהה יהיה https://example.com/app/.

זוהי שיטת האימות המומלצת אם נקודת הקצה מסוג HTTP לא מתארחת בשירות שתומך באימות שמבוסס על IAM (כמו Cloud Functions או Cloud Run). כשמשתמשים בשיטה הזו, שירות ה-HTTP צריך מידע על כתובת ה-URL של נקודת הקצה שבה הוא פועל, אבל לא צריך מידע על מספר הפרויקט ב-Cloud.

בדוגמאות הבאות מוסבר איך לאמת באמצעות ספריית הלקוח של Google OAuth שאסימון למוכ"ז הונפק על ידי Google Chat וכי הוא מיועד לאפליקציה שלכם.

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 של מספר פרויקט

אם השדה Authentication Audience (קהל היעד לאימות) בהגדרת החיבור של אפליקציית Chat מוגדר כ-Project Number, אסימון ההרשאה למוכ"ז בבקשה הוא אסימון אינטרנט מסוג 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;
}