Verificar solicitações do Google Chat

Para apps do Google Chat criados em endpoints HTTP, esta seção explica como verificar se as solicitações ao seu endpoint vêm do Chat.

Para enviar eventos de interação ao endpoint do seu app Chat, o Google faz solicitações ao seu serviço. Para verificar se a solicitação está vindo do Google, o Chat inclui um token de portador no cabeçalho Authorization de cada solicitação HTTPS para seu endpoint. Por exemplo:

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

A string AbCdEf123456 no exemplo anterior é o token de autorização do portador. É um token criptográfico produzido pelo Google. O tipo do token de portador e o valor do campo audience dependem do tipo de público-alvo de autenticação selecionado ao configurar o app Chat.

Se você implementou o app do Chat usando o Cloud Functions ou o Cloud Run, o Cloud IAM processa a verificação de token automaticamente. Basta adicionar a conta de serviço do Google Chat como um invocador autorizado. Se o app implementar o próprio servidor HTTP, verifique o token de autenticação usando uma biblioteca de cliente da API do Google de código aberto:

Se o token não for verificado para o app Chat, seu serviço vai responder à solicitação com um código de resposta HTTPS 401 (Unauthorized).

Autenticar solicitações usando o Cloud Functions ou o Cloud Run

Se a lógica da função for implementada usando o Cloud Functions ou o Cloud Run, selecione URL do endpoint HTTP no campo Público-alvo da autenticação da configuração de conexão do app Chat e verifique se o URL do endpoint HTTP na configuração corresponde ao URL do endpoint do Cloud Functions ou do Cloud Run.

Em seguida, autorize a conta de serviço do Google Chat chat@system.gserviceaccount.com como um invocador.

As etapas a seguir mostram como usar o Cloud Functions (1ª geração):

Console

Depois de implantar a função no Google Cloud:

  1. No console do Google Cloud, acesse a página Cloud Functions:

    Acesse o Cloud Functions

  2. Na lista do Cloud Functions, clique na caixa de seleção ao lado da função de recebimento. (Não clique na função em si.)

  3. Clique em Permissões na parte superior da tela. O painel Permissões é aberto.

  4. Clique em Adicionar principal.

  5. No campo Novos participantes, insira chat@system.gserviceaccount.com.

  6. Selecione o papel Cloud Functions > Invocador do Cloud Functions no menu suspenso Selecionar um papel.

  7. Clique em Salvar.

gcloud

Use o comando gcloud functions add-iam-policy-binding:

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

Substitua RECEIVING_FUNCTION pelo nome da função do seu app de chat.

As etapas a seguir mostram como usar os serviços do Cloud Functions (2ª geração) ou do Cloud Run:

Console

Depois de implantar a função ou o serviço no Google Cloud:

  1. No console do Google Cloud, acesse a página do Cloud Run:

    Acessar o Cloud Run

  2. Na lista de serviços do Cloud Run, clique na caixa de seleção ao lado da função de recebimento. (Não clique na função em si.)

  3. Clique em Permissões na parte superior da tela. O painel Permissões é aberto.

  4. Clique em Adicionar principal.

  5. No campo Novos participantes, insira chat@system.gserviceaccount.com.

  6. Selecione o papel Cloud Run > Chamador do Cloud Run no menu suspenso Selecionar um papel.

  7. Clique em Salvar.

gcloud

Use o comando gcloud functions add-invoker-policy-binding:

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

Substitua RECEIVING_FUNCTION pelo nome da função do seu app de chat.

Autenticar solicitações HTTP com um token de ID

Se o campo "Público-alvo da autenticação" da configuração de conexão do app Chat estiver definido como URL do endpoint HTTP, o token de autorização de portador na solicitação será um token de ID do OpenID Connect (OIDC) assinado pelo Google. O campo email está definido como chat@system.gserviceaccount.com. O campo Público da autenticação é definido como o URL que você configurou para o Google Chat enviar solicitações ao app. Por exemplo, se o endpoint configurado do app do Chat for https://example.com/app/, o campo Público da autenticação no token de ID será https://example.com/app/.

Esse é o método de autenticação recomendado se o endpoint HTTP não estiver hospedado em um serviço que ofereça suporte à autenticação baseada no IAM, como o Cloud Functions ou o Cloud Run. Ao usar esse método, seu serviço HTTP precisa de informações sobre o URL do endpoint em que está sendo executado, mas não precisa de informações sobre o número do projeto do Google Cloud.

Os exemplos a seguir mostram como verificar se o token de acesso foi emitido pelo Google Chat e direcionado ao seu app usando a biblioteca de cliente OAuth do 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;
}

Autenticar solicitações com um JWT de número do projeto

Se o campo "Público-alvo da autenticação" da configuração de conexão do app Chat estiver definido como Project Number, o token de autorização de portador na solicitação será um JSON Web Token (JWT) autoassinado, emitido e assinado por chat@system.gserviceaccount.com. O campo audience é definido como o número do projeto do Google Cloud usado para criar o app do Chat. Por exemplo, se o número do projeto do Cloud do app do Chat for 1234567890, o campo audience no JWT será 1234567890.

Esse método de autenticação só é recomendado se você preferir usar o número do projeto do Cloud para verificar solicitações em vez do URL do endpoint HTTP. Por exemplo, se você quiser mudar o URL do endpoint ao longo do tempo, mantendo o mesmo número de projeto do Cloud, ou se quiser usar o mesmo endpoint para vários números de projeto do Cloud e comparar o campo audience com uma lista de números de projeto do Cloud.

Os exemplos a seguir mostram como verificar se o token de portador foi emitido pelo Google Chat e direcionado ao seu projeto usando a biblioteca de cliente do 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;
}