skip to Main Content

I am following this tutorial https://learn.microsoft.com/en-us/azure/communication-services/tutorials/postman-tutorial
Using this same code, I called https://learn.microsoft.com/en-us/rest/api/communication/callautomation/create-call/create-call?view=rest-communication-callautomation-2023-10-15&tabs=HTTP and it works perfectly.

Next step, I called https://learn.microsoft.com/en-us/rest/api/communication/callautomation/call-media/play?view=rest-communication-callautomation-2023-10-15&tabs=HTTP with the callConnectionId I obtained from the previous API call.. and I keep getting

{
    "error": {
        "code": "7509",
        "message": "HMAC-SHA256 validation failed"
    }
}

My body for CallMedia-> play is

{
  "playSources": [
    {
      "kind": "text",
      "text": {"voiceKind": "male", "text": "Hello. This is Microsoft calling. If you are trying to authenticate, please press the pound or hash key now", "sourceLocale" : "en-US"}
    }
  ],
  "playTo": [
    {
        "phoneNumber": {
            "value": "+11231230672"
        }
    }
   ]
}

Where am I going wrong?

I tried changing

pm.request.headers.upsert({
    key:'Authorization',
    value: "HMAC-SHA256 SignedHeaders=date;host;x-ms-content-sha256&Signature=" + signature
});

to

pm.request.headers.upsert({ key:'Authorization', value: "HMAC-SHA256 Credential=" + pm.variables.get("credential") + "&SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=" + signature });

But that leads to an "invalid header parameter"

I tried hardcoding endpoint, url .. but no luck.

Can you please help?

2

Answers


  1. Chosen as BEST ANSWER

    The final solution was this

    Postman pre-request script

        // Set the Date header to our Date as a UTC String.
        const dateStr = new Date().toUTCString();
        pm.request.headers.upsert({key:'x-ms-date', value: dateStr});
        
        // Hash the request body using SHA256 and encode it as Base64
        const hashedBodyStr = CryptoJS.SHA256(pm.request.body.raw).toString(CryptoJS.enc.Base64)
        // And add that to the header x-ms-content-sha256
        pm.request.headers.upsert({
           key:'x-ms-content-sha256',
           value: hashedBodyStr
        });
        
        // Get our previously specified endpoint variable
        const endpoint = pm.variables.get('endpoint')
        // Remove the https, prefix to create a suitable "Host" value
        const hostStr = endpoint.replace('https://','');
        
        // This gets the part of our URL that is after the endpoint, for example in https://contoso.communication.azure.com/sms, it will get '/sms'
        //const url = pm.request.url.toString().replace('{{endpoint}}','');
        var url = pm.variables.replaceIn(pm.request.url.toString());
        // console.log('url here ', url)
        // console.log('endpoint ', endpoint);
        url = url.replace(endpoint,'');
        
        console.log('url final', url)
        
        // Construct our string which we'll sign, using various previously created values.
        const stringToSign = pm.request.method + 'n' + url + 'n' + dateStr + ';' + hostStr + ';' + hashedBodyStr;
        //console.log('stringToSign', stringToSign)
        // Decode our access key from previously created variables, into bytes from base64.
        const key = CryptoJS.enc.Base64.parse(pm.variables.get('key'));
        //console.log('stringToSign', stringToSign)
        // Sign our previously calculated string with HMAC 256 and our key. Convert it to Base64.
        const signature = CryptoJS.HmacSHA256(stringToSign, key).toString(CryptoJS.enc.Base64);
        
        // Add our final signature in Base64 to the authorization header of the request.
        pm.request.headers.upsert({
           key:'Authorization',
           value: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=" + signature
        });
    

    Call request post-response script

        const responseJson = pm.response.json();
        // Stores the callConnectionId in an environment or global variable
        var callConnectionId = responseJson.callConnectionId;
        pm.environment.set("callConnectionId", callConnectionId);
    

    Call request body

        {
         "callbackUri": "https://sub.domain.com/v0/CallBack/Add",
         "sourceCallerIdNumber": {
           "value": "+18441231234"
         },
         "targets": [
           {
             "phoneNumber": {
               "value": "+11231231234"
             }
           }
         ],
         "callIntelligenceOptions": {"cognitiveServicesEndpoint" : "https://yourresource.cognitiveservices.azure.com"}
        }
    

    Recognize API call

    Has headers added for Ocp-Apim-Subscription-Key = User-Agent : Something Voice OTP

    Body

        {
           "recognizeInputType": "dtmf",
           "playPrompt": {
               "kind": "file",
               "file": {
                   "uri": "https://some-url/some-voice-otp.wav"
               }
           },
           "recognizeOptions": {
               "interruptPrompt": true,
               "initialSilenceTimeoutInSeconds": 5,
               "targetParticipant": {
                       "kind": "phoneNumber",
                       "phoneNumber": {
                           "value": "+11231231234"
                       }
                   },
               "dtmfOptions": {
                   "interToneTimeoutInSeconds": 35,
                   "maxTonesToCollect": 1,
                   "stopTones": [
                       "asterisk"
                   ]
               }
           }
        }
    

    RETRY Recognize API call

    Has headers added for Ocp-Apim-Subscription-Key = User-Agent : some Voice OTP

    Body

        {
           "recognizeInputType": "dtmf",
           "playPrompt": {
               "kind": "file",
               "file": {
                   "uri": "https://some-url/some-voice-otp-tryagain.wav"
               }
           },
           "recognizeOptions": {
               "interruptPrompt": true,
               "initialSilenceTimeoutInSeconds": 5,
               "targetParticipant": {
                       "kind": "phoneNumber",
                       "phoneNumber": {
                           "value": "+11231231234"
                       }
                   },
               "dtmfOptions": {
                   "interToneTimeoutInSeconds": 35,
                   "maxTonesToCollect": 1,
                   "stopTones": [
                       "asterisk"
                   ]
               }
           }
        }
    

    DECLINE auth API call

    Has headers added for Ocp-Apim-Subscription-Key = User-Agent : Some Voice OTP

    Body

        {
         "playSources": [
           {
             "kind": "file",
             "file": {
               "uri": "https://some-url/some-voice-otp-declined.wav"
             }
           }
         ],
         "playTo": [
           {
               "kind": "phoneNumber",
               "phoneNumber": {
                   "value": "+1231231234"
               }
           }    
          ],
         "operationCallbackUri": "https://some-url.com/v0/CallBack/Add"
        }
    

    Terminate auth API call

    Has headers added for Ocp-Apim-Subscription-Key = User-Agent : BHPS Voice OTP

    Body

    None


  2. The issue with HMAC-SHA256 validation when trying to make a call to the Azure Communication Services (ACS) API for playing media during a call due to invalid key details.

    The following are steps to CallMedia play:

    • Create and configure a Postman collection
    • Add the pre-request script from this link
    // Set the Date header to our Date as a UTC String.
    const dateStr = new Date().toUTCString();
    pm.request.headers.upsert({key:'Date', value: dateStr});
    
    // Hash the request body using SHA256 and encode it as Base64
    const hashedBodyStr = CryptoJS.SHA256(pm.request.body.raw).toString(CryptoJS.enc.Base64)
    // And add that to the header x-ms-content-sha256
    pm.request.headers.upsert({
        key:'x-ms-content-sha256',
        value: hashedBodyStr
    });
    
    // Get our previously specified endpoint variable
    const endpoint = pm.variables.get('endpoint')
    // Remove the https, prefix to create a suitable "Host" value
    const hostStr = endpoint.replace('https://','');
    
    // This gets the part of our URL that is after the endpoint, for example in https://contoso.communication.azure.com/sms, it will get '/sms'
    const url = pm.request.url.toString().replace('{{endpoint}}','');
    
    // Construct our string which we'll sign, using various previously created values.
    const stringToSign = pm.request.method + 'n' + url + 'n' + dateStr + ';' + hostStr + ';' + hashedBodyStr;
    
    // Decode our access key from previously created variables, into bytes from base64.
    const key = CryptoJS.enc.Base64.parse(pm.variables.get('key'));
    // Sign our previously calculated string with HMAC 256 and our key. Convert it to Base64.
    const signature = CryptoJS.HmacSHA256(stringToSign, key).toString(CryptoJS.enc.Base64);
    
    // Add our final signature in Base64 to the authorization header of the request.
    pm.request.headers.upsert({
        key:'Authorization',
        value: "HMAC-SHA256 SignedHeaders=date;host;x-ms-content-sha256&Signature=" + signature
    });
    
    • Connect the Cognitive Services to the Communication Service
      Enter image description here
      Add collection variables for Call Setup and create two variables:

    • key – This variable should be one of your keys from your Azure Communication Services’ key page within the Azure portal. For example, oW...A==.

    • endpoint – This variable should be your Azure Communication Services’ endpoint from the key page. Ensure you remove the trailing slash. For example, https://contoso.communication.azure.com.

    Enter image description here

    Call Setup with Cognitive Services:

    POST https://contoso.communications.azure.com/calling/callConnections?api-version=2023-10-15
    Authorization: Bearer {access_token}
    
    {
      "callbackUri": "https://app.contoso.com/callback",
      "targets": [
        {
          "kind": "phoneNumber",
          "phoneNumber": {
            "value": "+919"
          }
        }
      ],
      "sourceCallerIdNumber": {
        "value": "+183"
      },
      "sourceDisplayName": "Contoso Support",
      "callIntelligenceOptions": {
        "cognitiveServicesEndpoint": "https://<your-cognitive-service-endpoint>.cognitiveservices.azure.com/"
      }
    }
    

    Enter image description here

    Play Call with Text-to-Speech:

    POST https://contoso.communications.azure.com/calling/callConnections/{callConnectionId}:play?api-version=2023-10-15
    Authorization: Bearer {access_token}
    
    {
      "playSources": [
        {
          "kind": "text",
          "text": {
            "voiceKind": "male",
            "text": "Hello. This is Microsoft calling. If you are trying to authenticate, please press the pound or hash key now",
            "sourceLocale": "en-US"
          }
        }
      ],
      "playTo": [
        {
          "kind": "phoneNumber",
          "phoneNumber": {
            "value": "+919"
          }
        }
      ],
      "playOptions": {
        "loop": true
      },
      "operationCallbackUri": "https://app.contoso.com/callback"
    }
    

    Output:

    Enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search