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 do Chat, o Google faz solicitações HTTPS ao seu serviço. Para verificar se a solicitação está vindo do Google, o Chat inclui um token de ID do OpenID Connect (OIDC) assinado pelo Google como um token de portador no cabeçalho Authorization de cada solicitação HTTPS (e no campo authorizationEventObject.systemIdToken do corpo da solicitação). 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. Esse token criptográfico é assinado pela conta de serviço exclusiva por projeto do seu app do Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com), e o campo audience é definido como o URL do endpoint HTTP configurado para seu app do Chat ao configurar o app.

Você pode copiar o endereço de e-mail da conta de serviço do app do Chat na seção Configurações de conexão da guia Configuração da API Chat no console do Google Cloud:

  1. No console do Google Cloud, acesse Menu > APIs e serviços > APIs e serviços ativados > API Google Chat > Configuração:

    Acessar a configuração da API Google Chat

  2. Em Recursos interativos > Configurações de conexão, copie o e-mail da conta de serviço (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

Se você implementou o app do Chat usando funções do Cloud Run ou o Cloud Run, o Cloud IAM processa a verificação de token automaticamente quando você concede à conta de serviço do app do Chat o papel Cloud Run Invoker (roles/run.invoker). Se o app implementar o próprio servidor HTTP, você poderá verificar o token do portador usando uma biblioteca de cliente da API 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 Run functions

Se a lógica da função for implementada usando o Cloud Run functions ou o Cloud Run, verifique se os URLs de endpoint HTTP configurados em Gatilhos nas configurações de conexão do app Chat correspondem ao URL do endpoint da função do Cloud Run.

Em seguida, autorize a conta de serviço do seu app do Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com, copiada da seção Configurações de conexão da guia Configuração da API Chat) como um invocador seguindo estas etapas:

Console

Depois de implantar sua função ou 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 principais, insira o endereço de e-mail da conta de serviço do app de chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com).

  6. No menu Selecionar um papel, escolha Cloud Run.

    Chamador do Cloud Run.

  7. Clique em Salvar.

gcloud

Use o comando 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'

Substitua:

  • RECEIVING_FUNCTION: o nome da função do seu app de chat.
  • PROJECT_NUMBER: o número do projeto no endereço de e-mail da conta de serviço do app Chat.

Autenticar solicitações HTTP com um token de ID

Para endpoints HTTP, o token de autorização de portador na solicitação é um token de ID do OpenID Connect (OIDC) assinado pelo Google. O campo email é definido como o endereço de e-mail da conta de serviço do app do Chat (service-<var>PROJECT_NUMBER</var>@gcp-sa-gsuiteaddons.iam.gserviceaccount.com), e o campo audience é definido como o URL do endpoint HTTP configurado para receber a solicitação. Por exemplo, se o endpoint configurado do seu app de chat for https://example.com/app/, o campo audience 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 Run.

Os exemplos a seguir mostram como verificar se o token do portador foi emitido pelo Google para seu app do Chat e direcionado ao endpoint do app usando a biblioteca de cliente OAuth do Google:

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

Apps do Chat que não são complementos: verificar solicitações do Google Chat

A documentação a seguir se aplica a apps do Chat que não são complementos do Google Workspace. Para migrar um app do Chat que não é um complemento, consulte Converter um app do Google Chat em um complemento do Google Workspace.

Para apps do Chat que não são complementos configurados com URL do endpoint HTTP em Configurações de conexão, o tipo do token do portador e o valor do campo audience dependem do tipo de público-alvo de autenticação selecionado ao configurar o app do Chat, e as solicitações são assinadas pela conta de serviço compartilhada chat@system.gserviceaccount.com.

Autenticar solicitações usando o Cloud Run functions (apps do Chat que não são complementos)

Se a lógica da função for implementada usando o Cloud Run Functions, 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 da função do Cloud Run.

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

Console

Depois de implantar sua função ou 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. No menu Selecionar um papel, escolha Cloud Run.

    Chamador do Cloud Run.

  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 (apps do Chat que não são complementos)

Se o campo Público-alvo da autenticação da configuração de conexão do app do Chat que não é um complemento 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-alvo da autenticação é definido como o URL que você configurou para o Google Chat enviar solicitações ao seu app do Chat que não é um complemento. Por exemplo, se o endpoint configurado do seu app de chat for https://example.com/app/, o campo Público-alvo da autenticação no token de ID será https://example.com/app/.

Os exemplos a seguir mostram como verificar se o token do portador foi emitido pelo Google Chat e direcionado ao seu app do Chat, que não é um complemento, 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 (apps de chat que não são complementos)

Se o campo Público-alvo da autenticação da configuração de conexão do app Chat que não é um complemento estiver definido como Project Number, o token de autorização do 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 na nuvem do Google Cloud usado para criar seu app do Chat que não é um complemento. Por exemplo, se o número do projeto na nuvem do app de chat for 1234567890, o campo audience no JWT será 1234567890.

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

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