Skip to main content

Request authentication

A notification request from webhooks can be verified by using the host, x-ms-date, x-ms-content-sha256, and authorization headers with the unique secret received when the webhook was registered. The authorization is a sha256 HMAC hash of the request date, content, and URI that is created using the webhook secret.

Summary pseudocode

let contentHash = hash(serializedContent)

let signatureText = POST\n{pathAndQuery}\n{date};{authority};{contentHash}
let signature = sign(secret, signatureText)
let authorization = HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}

Sample values

Given values
let secret = A0+AeKBRG2KRGvnNwJpQlb6IJFk48CKXCIcrLoHncVJKDILsQSxS6NWCccwWm6r6FhGKhiHTBsG2wo/xU6FY/A==
let url = https://webhook.site/e2cee29b-012e-4f1d-8ef4-e95fd74a7a63
let content = {"some-unique-content":"ee6e441b-cc4a-46f8-895d-a5af79bcc233/hello-world"}
let date = Thu, 30 Mar 2023 08:38:32 GMT

The following headers would be valid
x-ms-date = Thu, 30 Mar 2023 08:38:32 GMT
x-ms-content-sha256 = lNlsp1XA03N34HrQsVzPgJKtC+r7l/RBF4V3JQUWMj4=
Authorization = HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=agAiSyogQbDHpeucoNwYz+yAr5nJ+v+zasdkSbqzv+U=

Working C# sample code

using System.Security.Cryptography;
using System.Text;

public class Examples
{
[Fact]
public void Verifying_Hmac_Headers()
{
var secret = "A0+AeKBRG2KRGvnNwJpQlb6IJFk48CKXCIcrLoHncVJKDILsQSxS6NWCccwWm6r6FhGKhiHTBsG2wo/xU6FY/A==";

var requestUrl = new Uri("https://webhook.site/e2cee29b-012e-4f1d-8ef4-e95fd74a7a63");
var actualContent = """{"some-unique-content":"ee6e441b-cc4a-46f8-895d-a5af79bcc233/hello-world"}""";
var actualContentHash = "lNlsp1XA03N34HrQsVzPgJKtC+r7l/RBF4V3JQUWMj4=";
var actualDate = "Thu, 30 Mar 2023 08:38:32 GMT";
var actualAuthorization = "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=agAiSyogQbDHpeucoNwYz+yAr5nJ+v+zasdkSbqzv+U=";

// Verify content hash
var contentHashInBytes = SHA256.HashData(Encoding.UTF8.GetBytes(actualContent));
var expectedContentHash = Convert.ToBase64String(contentHashInBytes);
Assert.Equal(expected: expectedContentHash, actual: actualContentHash);

// Verify signature
var expectedSignedString = $"POST\n{requestUrl.PathAndQuery}\n{actualDate};{requestUrl.Authority};{actualContentHash}";

using var hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hmacSha256Bytes = Encoding.UTF8.GetBytes(expectedSignedString);
var hmacSha256Hash = hmacSha256.ComputeHash(hmacSha256Bytes);
var expectedSignature = Convert.ToBase64String(hmacSha256Hash);
var expectedAuthorization = $"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={expectedSignature}";

Assert.Equal(expected: expectedAuthorization, actual: actualAuthorization);
}
}