Posts tagged with whatsapp-flows

The issue

I am trying to setup a flow with WhatsApp Business API but am unable to complete the decryption of the key as part of the endpoint healthcheck.

I get this error: Encryption/decryption failed

in decrypt_request     aes_key = private_key.decrypt(encrypted_aes_key, OAEP(mgf=MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) ValueError: Encryption/decryption failed. 

How to reproduce the issue

Below are the steps I followed, as per the docs here and here:

  1. Set up the flows endpoint.

  2. Generate 2048-bit RSA Key Pair.

openssl genrsa -des3 -out private.pem 2048 

The key looks like this:

-----BEGIN ENCRYPTED PRIVATE KEY----- AAA........ BBB.... .... -----END ENCRYPTED PRIVATE KEY----- 
  1. Export the RSA Public Key to a file:
openssl rsa -in private.pem -outform PEM -pubout -out public.pem 
  1. Set business public key.
curl -X POST \   'https://graph.facebook.com/v21.0/PHONE_NUMBER_ID/whatsapp_business_encryption' \   -H 'Authorization: Bearer ACCESS_TOKEN' \   -H 'Content-Type: application/x-www-form-urlencoded' \   --data-urlencode 'business_public_key=-----BEGIN PUBLIC KEY----- AAA BBB CCC -----END PUBLIC KEY-----' 
  1. Create encryption/decryption code:
import json import os from base64 import b64decode, b64encode from cryptography.hazmat.primitives.asymmetric.padding import OAEP, MGF1, hashes from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes from cryptography.hazmat.primitives.serialization import load_pem_private_key from fastapi import HTTPException, Request import logging # Load the private key string # PRIVATE_KEY = os.environ.get('PRIVATE_KEY') PRIVATE_KEY_PATH = "./private.pem" with open(PRIVATE_KEY_PATH, "rb") as pem_file:          PRIVATE_KEY = pem_file.read().decode("utf-8") async def data(request: Request):     try:         # Parse the request body         body = json.loads(await request.body())         logging.info(f"body = {body}")         # Read the request fields         encrypted_flow_data_b64 = body['encrypted_flow_data']         encrypted_aes_key_b64 = body['encrypted_aes_key']         initial_vector_b64 = body['initial_vector']         decrypted_data, aes_key, iv = await decrypt_request(             encrypted_flow_data_b64, encrypted_aes_key_b64, initial_vector_b64)         print(decrypted_data)         # Return the next screen & data to the client         response = {             "screen": "SCREEN_NAME",             "data": {                 "some_key": "some_value"             }         }         # Return the response as plaintext         return await encrypt_response(response, aes_key, iv)     except Exception as e:         print(e)         raise HTTPException(status_code=500, detail="Internal Server Error") async def decrypt_request(encrypted_flow_data_b64, encrypted_aes_key_b64, initial_vector_b64):     flow_data = b64decode(encrypted_flow_data_b64)     iv = b64decode(initial_vector_b64)     # Decrypt the AES encryption key     encrypted_aes_key = b64decode(encrypted_aes_key_b64)     private_key = load_pem_private_key(         PRIVATE_KEY.encode("utf-8"), password=os.getenv("PASSPHRASE").encode('utf-8'))     aes_key = private_key.decrypt(encrypted_aes_key, OAEP(         mgf=MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))     # Decrypt the Flow data     encrypted_flow_data_body = flow_data[:-16]     encrypted_flow_data_tag = flow_data[-16:]     decryptor = Cipher(algorithms.AES(aes_key),                        modes.GCM(iv, encrypted_flow_data_tag)).decryptor()     decrypted_data_bytes = decryptor.update(         encrypted_flow_data_body) + decryptor.finalize()     decrypted_data = json.loads(decrypted_data_bytes.decode("utf-8"))     return decrypted_data, aes_key, iv async def encrypt_response(response, aes_key, iv):     # Flip the initialization vector     flipped_iv = bytearray()     for byte in iv:         flipped_iv.append(byte ^ 0xFF)     # Encrypt the response data     encryptor = Cipher(algorithms.AES(aes_key),                        modes.GCM(flipped_iv)).encryptor()     return b64encode(         encryptor.update(json.dumps(response).encode("utf-8")) +         encryptor.finalize() +         encryptor.tag     ).decode("utf-8") 
  1. Trigger the health check from https://business.facebook.com/latest/whatsapp_manager/flows

Can someone tell me what I'm doing wrong?

I am using the WhatsApp Cloud API to create and send forms using the Flow feature. The forms are successfully created and are accessible by other users without any issues. However, when I try to open the form on my own WhatsApp Business App, it gets stuck on the loading screen and never fully loads. Even simple test messages like "Hello World" fail to display.

Here are the steps I've taken:

  1. Created a form using JSON.
  2. Verified that the JSON is correctly formatted.
  3. Confirmed that the form works on other users' devices.
  4. Updated my WhatsApp Business App to the latest version.
  5. I have a good internet connection

Example JSON:

{     "version": "3.1",     "screens": [         {             "id": "WELCOME_SCREEN",             "layout": {                 "type": "SingleColumnLayout",                 "children": [                     {                         "type": "TextHeading",                         "text": "Hello World"                     },                     {                         "type": "TextBody",                         "text": "Let's start building things!"                     },                     {                         "type": "Footer",                         "label": "Complete",                         "on-click-action": {                             "name": "complete",                             "payload": {}                         }                     }                 ]             },             "title": "Welcome",             "terminal": true,             "success": true,             "data": {}         }     ] } 

Log Information:

[2024-08-07 16:38:01] local.DEBUG: Sending message to WhatsApp API  [{"stdClass":{"type":"interactive","interactive":{"type":"flow","body":{"text":"Hello word template"},"action":{"name":"flow","parameters":{"mode":"draft","flow_message_version":"3","flow_token":"aiqvYS","flow_id":"11655894848158","flow_cta":"Me Test","flow_action":"navigate","flow_action_payload":{"screen":"WELCOME_SCREEN"}}},"header":{"type":"text","text":"Welcome to the BotController"}},"to":"2637123456","messaging_product":"whatsapp","recipient_type":"individual"}}] 

Has anyone encountered this issue before or have any suggestions on how to resolve it? Any insights or solutions would be greatly appreciated.

Additional Info:

  • My device is running the latest version of the WhatsApp Business App.
  • Basic flow forms also fail to load on my device.

Thank you in advance!

I'm developing a Flask app to store data from my clients (Datakinder parents). I am receiving the request and decrypting the message successfully. But I always receive incorrect data On on-click-action' I expect to receive user typed data but I always receive ${form.parentname} , not the user data

Footer code is:

                        "type": "Footer",                         "label": "Siguiente",                         "on-click-action": {                             "name": "data_exchange",                             "payload" : {                                 "parentname":"${form.parentname}"                             }                         },                         "enabled":true                      } 

At clicked "Siguiente" (next), endpoint receive data={'parentname': '${form.parentname}'} it must be : data={'parentname': 'Alejandro'}

Any Idea?

I sent fixed data in payload and it was received properly, but form data is not

im failing to decrypt the response from the whatsapp flow, and im using php, codeigniter 4 with the phpseclib3 library, and below is my sample code:

private function decryptRequest($body, $privatePem) {     $encryptedAesKey   = base64_decode($body['encrypted_aes_key']);     $encryptedFlowData = base64_decode($body['encrypted_flow_data']);     $initialVector     = base64_decode($body['initial_vector']);     $rsa = RSA::load($privatePem)         ->withPadding(RSA::ENCRYPTION_OAEP)         ->withHash('sha256')         ->withMGFHash('sha256');                          $decryptedAesKey = $rsa->decrypt($encryptedAesKey);              if (!$decryptedAesKey) {         throw new Exception('Decryption of AES key failed.');     }          $aes = new AES('gcm');     $aes->setKey($decryptedAesKey);     $aes->setNonce($initialVector);          $decrypted = $aes->decrypt($encryptedFlowData);     if (!$decrypted) {         throw new Exception('Decryption of flow data failed.');     }          return [         'decryptedBody' => json_decode($decrypted, true),         'aesKeyBuffer' => $decryptedAesKey,         'initialVectorBuffer' => $initialVector,     ]; } 

and im getting the Ciphertext representative too long error on this line: $decryptedAesKey = $rsa->decrypt($encryptedAesKey);

I have tried to refer to the docementation here https://developers.facebook.com/docs/whatsapp/cloud-api/reference/whatsapp-business-encryption

I'm trying to generate the response for the WhatsApp flow using the WhatsApp business API with the following code

The decryption part is functioning correctly, but when I attempt to send the response, I'm receiving the error: "Could not decrypt the response received from the server."

I've referred to the documentation here, but I'm still struggling to find the correct approach for generating and validating the response.

Is there anyone who has experience with this API or can provide guidance on how to properly format and send the response? Any examples or links to relevant resources would be greatly appreciated.

def post(self, request, *args, **kwargs):         try:             dict_data = json.loads(request.body.decode('utf-8'))             encrypted_flow_data_b64 = dict_data['encrypted_flow_data']             encrypted_aes_key_b64 = dict_data['encrypted_aes_key']             initial_vector_b64 = dict_data['initial_vector']                          flipped_iv = self.flip_iv(initial_vector_b64.encode('utf-8'))                          encrypted_aes_key = b64decode(encrypted_aes_key_b64)             key_private = open('*******.pem', 'rb').read().decode('utf-8')             private_key = load_pem_private_key(key_private.encode('utf-8'), password="*************".encode('utf-8'))                          aes_key = private_key.decrypt(encrypted_aes_key, OAEP(mgf=MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))             aes_key_b64 = b64encode(aes_key).decode('utf-8')                          flow_data  = b64decode(encrypted_flow_data_b64)             key = b64decode(aes_key_b64)             iv = b64decode(initial_vector_b64)                          encrypted_flow_data_body = flow_data[:-16]             encrypted_flow_data_tag = flow_data[-16:]             cipher = Cipher(algorithms.AES(key), modes.GCM(iv,encrypted_flow_data_tag))             decryptor = cipher.decryptor()             decrypted_data = decryptor.update(encrypted_flow_data_body) + decryptor.finalize()             flow_data_request_raw = decrypted_data.decode("utf-8")                          hello_world_text = "HELLO WORLD"                          response_data = {                 "version": "3.0",                 "screen": "MY_FIRST_SCREEN",                 "data": {                     "hello_world_text": hello_world_text                 }             }             response_json = json.dumps(response_data)                          # Obtendo a chave AES após descriptografar encrypted_aes_key             fb_aes_key = private_key.decrypt(encrypted_aes_key, OAEP(mgf=MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))             # Usando a chave AES para criptografar a resposta             response_cipher = Cipher(algorithms.AES(fb_aes_key), modes.GCM(iv))             encryptor = response_cipher.encryptor()             encrypted_response = (                 encryptor.update(response_json.encode("utf-8")) +                 encryptor.finalize() +                 encryptor.tag             )             encrypted_response_b64 = b64encode(encrypted_response).decode("utf-8")                          # Construct the final response             final_response = {                 "encrypted_flow_data": encrypted_response_b64,                 "encrypted_aes_key": encrypted_aes_key_b64,                 "initial_vector": initial_vector_b64             }                          return JsonResponse(final_response, status=200)         except Exception as e:             print(e)             return HttpResponse(status=500, content='ok')          def flip_iv(self, iv):         flipped_bytes = []         for byte in iv:             flipped_byte = byte ^ 0xFF             flipped_bytes.append(flipped_byte)         return bytes(flipped_bytes) 

The entire decoding part is working normally but when returning the response I receive the error "Could not decrypt the response received from the server. "I can't find how to send the correct answer or how to validate it. The documentation can be found at https://developers.facebook.com/docs/whatsapp/flows/reference/implementingyourflowendpoint#data_exchange_request

Can anyone help me or show me a link I can test?