Google Chat'ten gelen istekleri doğrulama

HTTP uç noktaları üzerinde oluşturulan Google Chat uygulamaları için bu bölümde, uç noktanıza yapılan isteklerin Chat'ten geldiğini nasıl doğrulayacağınız açıklanmaktadır.

Google, etkileşim etkinliklerini Chat uygulamanızın uç noktasına göndermek için hizmetinize HTTPS istekleri gönderir. İsteğin Google'dan geldiğini doğrulamak için Chat, her HTTPS isteğinin Authorization üstbilgisinde (ve istek gövdesinin authorizationEventObject.systemIdToken alanında) hamiline ait jeton olarak Google imzalı bir OpenID Connect (OIDC) kimlik jetonu içerir. Örneğin:

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

Önceki örnekteki AbCdEf123456 dizesi, taşıyıcı yetkilendirme jetonudur. Bu kriptografik jeton, Chat uygulamanızın proje başına benzersiz hizmet hesabı (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) tarafından imzalanır ve audience alanı, Chat uygulamasını yapılandırırken Chat uygulamanız için yapılandırılan HTTP uç nokta URL'sine ayarlanır.

Google Cloud Console'daki Chat API Yapılandırma sekmesinin Bağlantı ayarları bölümünden Chat uygulamanızın hizmet hesabı e-posta adresini kopyalayabilirsiniz:

  1. Google Cloud Console'da Menü > API'ler ve Hizmetler > Etkin API'ler ve Hizmetler > Google Chat API > Yapılandırma'ya gidin:

    Google Chat API Yapılandırması'na gitme

  2. Etkileşimli özellikler > Bağlantı ayarları bölümünde Hizmet hesabı e-posta adresini (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) kopyalayın.

Chat uygulamanızı Cloud Run işlevlerini veya Cloud Run'ı kullanarak uyguladıysanız Chat uygulamasının hizmet hesabına Cloud Run Invoker (roles/run.invoker) rolünü verdiğinizde Cloud IAM, jeton doğrulamayı otomatik olarak gerçekleştirir. Uygulamanız kendi HTTP sunucusunu uyguluyorsa hamiline ait jetonu açık kaynaklı bir Google API istemci kitaplığı kullanarak doğrulayabilirsiniz:

Jeton, Sohbet uygulamasında doğrulanmazsa hizmetiniz isteğe bir HTTPS yanıt koduyla 401 (Unauthorized) yanıt vermelidir.

Cloud Run işlevlerini kullanarak isteklerin kimliğini doğrulama

İşlev mantığınız Cloud Run işlevleri veya Cloud Run kullanılarak uygulanıyorsa Chat uygulaması bağlantı ayarlarındaki Tetikleyiciler bölümünde yapılandırılan HTTP uç nokta URL'lerinin Cloud Run işlevi uç noktanızın URL'sine karşılık geldiğinden emin olun.

Ardından, aşağıdaki adımları uygulayarak Chat uygulamanızın hizmet hesabını (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, Chat API'nin Yapılandırma sekmesindeki Bağlantı ayarları bölümünden kopyalanmıştır) çağıran olarak yetkilendirin:

Konsol

İşlevinizi veya hizmetinizi Google Cloud'a dağıttıktan sonra:

  1. Google Cloud Console'da Cloud Run sayfasına gidin:

    Cloud Run'a gitme

  2. Cloud Run hizmetleri listesinde, alıcı işlevin yanındaki onay kutusunu tıklayın. (İşlevin kendisini tıklamayın.)

  3. Ekranın üst kısmındaki İzinler'i tıklayın. İzinler paneli açılır.

  4. Ana hesap ekle'yi tıklayın.

  5. Yeni ana hesaplar alanına, Chat uygulamanızın hizmet hesabı e-posta adresini (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) girin.

  6. Rol seç menüsünden Cloud Run rolünü seçin.

    Cloud Run Invoker.

  7. Kaydet'i tıklayın.

gcloud

gcloud functions add-invoker-policy-binding komutunu kullanın:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:service-PROJECT_NUMBER@gcp-sa-gsuiteaddons.iam.gserviceaccount.com'

Aşağıdakini değiştirin:

  • RECEIVING_FUNCTION: Sohbet uygulamanızın işlevinin adı.
  • PROJECT_NUMBER: Chat uygulamanızın hizmet hesabı e-posta adresindeki proje numarası.

HTTP isteklerinin kimliğini kimlik jetonuyla doğrulama

HTTP uç noktaları için, isteğin taşıyıcı yetkilendirme jetonu, Google tarafından imzalanmış bir OpenID Connect (OIDC) kimlik jetonudur. email alanı, sohbet uygulamanızın hizmet hesabı e-posta adresi (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com) olarak ayarlanır. audience alanı ise isteği alacak şekilde yapılandırılmış HTTP uç nokta URL'si olarak ayarlanır. Örneğin, Chat uygulamanızın yapılandırılmış uç noktası https://example.com/app/ ise kimlik jetonundaki audience alanı https://example.com/app/ olur.

HTTP uç noktanız IAM tabanlı kimlik doğrulamayı destekleyen bir hizmette (ör. Cloud Run) barındırılmıyorsa bu kimlik doğrulama yöntemi önerilir.

Aşağıdaki örneklerde, Google OAuth istemci kitaplığını kullanarak hamiline ait jetonun Google tarafından Chat uygulamanız için verildiğini ve uygulamanızın uç noktasını hedeflediğini nasıl doğrulayacağınız gösterilmektedir:

Java

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

Eklenti olmayan Chat uygulamaları: Google Chat'ten gelen istekleri doğrulama

Aşağıdaki belgeler, Google Workspace eklentisi olmayan Chat uygulamaları için geçerlidir. Eklenti olmayan bir Chat uygulamasını taşımak için Google Chat uygulamasını Google Workspace eklentisine dönüştürme başlıklı makaleyi inceleyin.

Bağlantı ayarları bölümünde HTTP uç nokta URL'si ile yapılandırılmış eklenti olmayan Chat uygulamalarında, hamiline ait jetonun türü ve audience alanının değeri, Chat uygulamasını yapılandırırken seçtiğiniz kimlik doğrulama hedef kitlesinin türüne bağlıdır. İstekler, paylaşılan hizmet hesabı chat@system.gserviceaccount.com tarafından imzalanır.

Cloud Run işlevlerini kullanarak isteklerin kimliğini doğrulama (eklenti olmayan Chat uygulamaları)

İşlev mantığınız Cloud Run işlevleri kullanılarak uygulanıyorsa Chat uygulaması bağlantı ayarının Kimlik Doğrulama Hedef Kitlesi alanında HTTP uç noktası URL'si'ni seçmeniz ve yapılandırmadaki HTTP uç noktası URL'sinin Cloud Run işlevi uç noktasının URL'sine karşılık geldiğinden emin olmanız gerekir.

Ardından, aşağıdaki adımları uygulayarak Google Chat hizmet hesabını chat@system.gserviceaccount.com çağıran olarak yetkilendirmeniz gerekir:

Konsol

İşlevinizi veya hizmetinizi Google Cloud'a dağıttıktan sonra:

  1. Google Cloud Console'da Cloud Run sayfasına gidin:

    Cloud Run'a gitme

  2. Cloud Run hizmetleri listesinde, alıcı işlevin yanındaki onay kutusunu tıklayın. (İşlevin kendisini tıklamayın.)

  3. Ekranın üst kısmındaki İzinler'i tıklayın. İzinler paneli açılır.

  4. Ana hesap ekle'yi tıklayın.

  5. New principals (Yeni ana hesaplar) alanına chat@system.gserviceaccount.com girin.

  6. Rol seç menüsünden Cloud Run rolünü seçin.

    Cloud Run Invoker.

  7. Kaydet'i tıklayın.

gcloud

gcloud functions add-invoker-policy-binding komutunu kullanın:

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

RECEIVING_FUNCTION yerine Chat uygulamanızın işlevinin adını girin.

HTTP isteklerinin kimliğini kimlik jetonuyla doğrulama (eklenti olmayan Chat uygulamaları)

Eklenti olmayan bir sohbet uygulamasının bağlantı ayarının Kimlik Doğrulama Hedef Kitlesi alanı HTTP uç nokta URL'si olarak ayarlanmışsa isteğin taşıyıcı yetkilendirme jetonu, Google tarafından imzalanmış bir OpenID Connect (OIDC) kimlik jetonudur. email alanı chat@system.gserviceaccount.com olarak ayarlanır. Kimlik Doğrulama Kitlesi alanı, Google Chat'i eklenti olmayan Chat uygulamanıza istek gönderecek şekilde yapılandırdığınız URL olarak ayarlanır. Örneğin, sohbet uygulamanızın yapılandırılmış uç noktası https://example.com/app/ ise kimlik jetonundaki Kimlik Doğrulama Kitlesi alanı https://example.com/app/ olur.

Aşağıdaki örneklerde, Google OAuth istemci kitaplığı kullanılarak hamiline ait jetonun Google Chat tarafından verildiği ve eklenti olmayan Chat uygulamanızı hedeflediği nasıl doğrulanacağı gösterilmektedir.

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

İsteklerin kimliğini proje numarası JWT ile doğrulama (eklenti olmayan Chat uygulamaları)

Eklenti olmayan Chat uygulamasının Kimlik Doğrulama Hedef Kitlesi alanı bağlantı ayarı Project Number olarak ayarlanırsa isteğin taşıyıcı yetkilendirme jetonu, chat@system.gserviceaccount.com tarafından verilen ve imzalanan, kendi kendine imzalanmış bir JSON Web Token (JWT) olur. audience alanı, eklenti olmayan Chat uygulamanızı oluşturmak için kullandığınız Google Cloud proje numarası olarak ayarlanır. Örneğin, sohbet uygulamanızın Cloud proje numarası 1234567890 ise JWT'deki audience alanı 1234567890 olur.

Aşağıdaki örneklerde, Google OAuth istemci kitaplığı kullanılarak hamiline ait jetonun Google Chat tarafından verilip verilmediği ve projenizi hedefleyip hedeflemediğinin nasıl doğrulanacağı gösterilmektedir.

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