In this post, I’ll show you how to accept a signed Meadow.Cloud webhook from ASP.NET Core. I’ll also show how to verify the signature against the payload, as this is a necessary verification step that ensures the web request came from Meadow.Cloud and not by a malicious actor.
Prerequisites
- Meadow board
- Meadow.Cloud account
- ASP.NET Core application
- Visual Studio or Visual Studio Code
Among other things, you’ll need to have a Meadow board provisioned on a Meadow.Cloud account to follow along, since this is all about accepting a signed Meadow.Cloud webhook.
Accepting the webhook
To accept a signed Meadow.Cloud webhook, you’ll need to create an endpoint in your ASP.NET Core application that listens for the webhook. You can do this by creating a new controller and action method that accepts the webhook, or using a minimal api, which is the approach I’ll take here.
Visual Studio 2022 should make it fairly simple to create a new minimal api project, so I won’t go into details.
Reading the Meadow.Cloud documentation, you’ll find that Meadow.Cloud sends a POST, and signs the webhook with a secret key, and sends the signature in the X-MC-SIGNATURE-256
header. This signature is a hash of the payload and key if you’ve specified one. If you find that this header is empty, likely you’ve not specified a secret key in the Meadow.Cloud webhook configuration.
Here’s a minimal api that accepts a signed Meadow.Cloud webhook:
app.MapPost("webhook", ([FromHeader(Name ="X-MC-SIGNATURE-256")] string signature, [FromBody]string body) =>
{
string ComputeHash(string input, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(input));
return Convert.ToHexString(hashBytes).ToLower();
}
}
var secret = ""; // Your secret here
var localhash = ComputeHash(body, secret);
if (localhash.Equals(signature, StringComparison.OrdinalIgnoreCase))
{
try
{
// Signature matches, continue processing
return Results.Ok();
}
catch (JsonException ex)
{
return Results.BadRequest(ex.Message);
}
}
else
{
// Signature does not match, return error
return Results.BadRequest("Invalid signature");
}
});
Using ASP.NET model binding, the signature
parameter is bound to the X-MC-SIGNATURE-256
header, and the body
parameter is bound to the request body. The ComputeHash
method computes the hash of the payload and the secret key, and the localhash
variable stores the computed hash.
Keep in mind that your secret key should be stored in a secure location, and not hard-coded in your application as the example shows. That is outside the scope of this article to explain, but you might consider using a secure secret management system, such as Azure Key Vault, or AWS Secrets Manager.
I hope this article has been helpful to you. If you have any questions, the Wilderness Labs community is a great place to ask for help.