Visão geral
URLs de webhooks ficam expostas na internet. Para garantir que uma request realmente veio do iFood, implementamos o header X-IFood-Signature em todas as requests de webhook. Esse header permite validar a autenticidade das mensagens recebidas.Rejeite requests com assinaturas inválidas. Essa validação é obrigatória para homologação do webhook. O iFood pode enviar eventos com assinaturas inválidas para testar sua integração. Essa checagem também protege contra fraudes. Auditamos todos os eventos entregues, as tentativas de entrega e os eventos descartados.Validação
O webhook usa (HMAC) com SHA256 para assinar cada mensagem. O valor final é codificado em hexadecimal. A assinatura usa o clinte secret da aplicação (o mesmo utilizado para gerar tokens), disponível na aba "Credenciais" do Portal do Desenvolvedor. Cada request inclui essa assinatura no header X-IFood-Signature. Para validar, gere o HMAC da mensagem recebida usando seu secret (em local seguro) e compare com a assinatura recebida usando comparação segura (exemplo). A maioria das linguagens tem bibliotecas com suporte nativo:Validação antes do parsing
Campos podem ser adicionados a qualquer evento a qualquer momento, sem causar breaking change. Por isso, valide a assinatura antes de converter o conteúdo da mensagem em objeto. Gere a assinatura usando o byte array do body exatamente como recebido.Esse cuidado é essencial em JSON, já que {"prop1": "value1", "prop2": "value2"} e {"prop2": "value2", "prop1": "value1"}  são equivalentes para parsers, mas formam byte arrays diferentes. Caracteres especiais também podem variar conforme a biblioteca ou linguagem.Exemplos
Todos os exemplos são válidos para o mesmo JSON, mas com diferentes formatações e devem ser suportados pela sua integração:{
    "code":"PLC",
    "createdAt":"2023-02-20T18:19:03.20162269Z",
    "fullCode":"PLACED",
    "id":"a38ba215-f949-4b2c-982a-0582a9d0c10e",
    "merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a",
    "orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3"
}
Os exemplos mostram como a formatação afeta a assinatura:Sem espaços ou quebra de linha
 X-IFood-Signature: 6f9ed23a7b505a3b6907c5f6eb2ad1b056fbf35a643d365a9a072ed7aabca153.  Payload:{"code":"PLC","createdAt":"2023-02-20T18:19:03.20162269Z","fullCode":"PLACED","id":"a38ba215-f949-4b2c-982a-0582a9d0c10e","merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a","orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3"}
Java
Segue um snippet Java de como fazer a validação da mensagem:private String bytesToHexString(byte[] bytes) {
    var sb = new StringBuilder();
    for (var b : bytes) {
        var hex = String.format("%02x", b);
        sb.append(hex);
    }
    return sb.toString();
}
private boolean verifyHmacSHA256(String secret, String data, String expectedSignature) {
    try {
        var mac = Mac.getInstance("HmacSHA256");
        var secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKeySpec);
        var hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHexString(hmacBytes).equals(expectedSignature);
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        return false;
    }
}
Go
Veja um exemplo o snippet de um enpoint em Go:http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
  body, err := io.ReadAll(r.Body)
  if err != nil {
    fmt.Printf("could not read body")
    w.WriteHeader(400)
    return
  }
  headerSignature := r.Header.Get("X-IFood-Signature")
  signature, err := hex.DecodeString(headerSignature)
  if err != nil {
    panic(err)
  }
  hasher := hmac.New(sha256.New, SECRET)
  hasher.Write(body)
  expected := hasher.Sum(nil)
  if !hmac.Equal(expected, signature) {
    fmt.Printf("invalid signature")
    w.WriteHeader(401)
    return
  }
  // Message validated...
})