I am trying to connect to the Azure Communications API to send SMS messages using the REST endpoint.
The link to the API instructions is here: https://learn.microsoft.com/en-us/azure/azure-app-configuration/rest-api-authentication-hmac
My PHP code is as follows:
public static function send(array $phoneNumbers, string $message)
{
$body = json_encode([
'from' => config("azure.sms_phone_number"),
'message' => $message,
'smsRecipients' => array_map(fn ($num) => ['to' => $num], $phoneNumbers)
]);
$endpoint = parse_url(config("azure.sms_endpoint"));
$headers = [
'Date' => gmdate("D, d M y H:i:s T"),
'host' => $endpoint['host'],
'x-ms-content-sha256' => base64_encode(hash('sha256', $body, true)),
'Content-Type' => 'application/json',
'Accept' => '*/*'
];
$stringToSign = utf8_encode("POST\n" . $endpoint['path'] . "?" . $endpoint['query'] . "\n" . implode(";", array_values($headers)));
$headers['Authorization'] = implode("&", [
"HMAC-SHA256 Credential=" . config("azure.sms_key_id"),
"SignedHeaders=" . implode(";", array_keys($headers)),
'Signature=' . base64_encode(hash_hmac('sha256', $stringToSign, base64_decode(config("azure.sms_key")), true))
]);
$client = new Client();
$client->post(config("azure.sms_endpoint"), [
'headers' => $headers,
'body' => $body,
'debug' => true
]);
}
Below are some of the variables
$body = '{"from":"+1844295xxx","message":"hi","smsRecipients":[{"to":"2019160xxx"}]};';
$stringToSign = 'POST
/sms?api-version=2021-03-07
Mon, 14 Mar 22 17:09:17 GMT;testxxxxxx.communication.azure.com;UXoK8141pppkVedAkc+eSQBqKOWciyoiq+AG/xxxxxx=;application/json;*/*;';
$headers =
(
[Date] => Mon, 14 Mar 22 17:09:17 GMT
[host] => testxxxxx.communication.azure.com
[x-ms-content-sha256] => UXoK8141pppkVedAkc+eSQBqKOWciyoiq+AG/xxxxxx=
[Content-Type] => application/json
[Accept] => */*
[Authorization] => HMAC-SHA256 Credential=primaryKey&SignedHeaders=Date;host;x-ms-content-sha256;Content-Type;Accept&Signature=R8M7+fODzXaxXHbdcV5CHXiEq5R/7Fvd9VGYxxxxxxx=
)
The result I get is this:
Client error: `POST https://test.communication.azure.com/sms?api-version=2021-03-07` resulted in a `401 Unauthorized` response:
{"error":{"code":"Denied","message":"Given value does not match HMAC header structure."}}
The access keys and names come from this command:
PS C:\Users\manko> az communication list-key --name scotttestingsms --resource-group smsresourcegroup
{
"primaryConnectionString": "endpoint=https://scotttestingsms.communication.azure.com/;accesskey=E7lrdL/yyg/snSO++rbaZMEUF/bC5/0R9XBVGcFclt3fEN/MpWRb5kHB9t59NLtek9xsUYXHyAXxxxxxxx==",
"primaryKey": "E7lrdL/yyg/snSO++rbaZMEUF/bC5/0R9XBVGcFclt3fEN/MpWRb5kHB9t59NLtek9xsUYXHyAXxxxxxx==",
"secondaryConnectionString": "endpoint=https://scotttestingsms.communication.azure.com/;accesskey=IIK094eGVfkNG0uFii/32j+HVsEHJ4/QUOx06TVsqwLub7A/cv1AKKnkkZQbKiJKMn/KRx9o1biWQ5txxxxxx==",
"secondaryKey": "IIK094eGVfkNG0uFii/32j+HVsEHJ4/QUOx06TVsqwLub7A/cv1AKKnkkZQbKiJKMn/KRx9o1biWQ5txxxxxx=="
}
It took quite some time of trial and error, but the following code works. See https://learn.microsoft.com/en-us/azure/communication-services/tutorials/hmac-header-tutorial for more information.
use GuzzleHttp\Client;
public static function send(array $phoneNumbers, string $message)
{
$body = [
'from' => config("azure.sms_phone_number"),
'message' => $message,
'smsRecipients' => array_map(fn ($num) => ['to' => $num], $phoneNumbers)
];
$endpoint = parse_url(config("azure.sms_endpoint"));
$headers = [
'Date' => gmdate("D, d M Y H:i:s T"),
'host' => $endpoint['host'],
'x-ms-content-sha256' => base64_encode(hash('sha256', json_encode($body), true)),
];
$stringToSign = utf8_encode(implode("\n", [
"POST",
$endpoint['path'] . "?" . $endpoint['query'],
implode(";", array_values($headers))
]));
$headers['Authorization'] = implode("&", [
"HMAC-SHA256 SignedHeaders=" . implode(";", array_keys($headers)),
'Signature=' . base64_encode(hash_hmac('sha256', $stringToSign, base64_decode(config("azure.sms_key")), true))
]);
$client = new Client(); // <-- this is guzzle
$response = $client->post(config("azure.sms_endpoint"), [
'headers' => $headers,
'json' => $body
]);
}
You only need three pieces of data.
config("azure.sms_phone_number")
is the originating phone number. It must be in E.164 format, e.g. "+12024561414"config("azure.sms_endpoint")
is the full endpoint, e.g. "https://test.communication.azure.com/sms?api-version=2021-03-07"config("azure.sms_key")
is the application key copied right off the azure portal in base64, e.g. "E7lrdL/yyg/snSO++rbaZMEUF/d1G/0R9XBVGch0tq3xxxxxxxxxxxx=="