C# example code for authentication a web hook

Nov 24, 2013 at 12:42 AM
Hi

has anyone got any example code of how to authenticate a Mandrill web hook with c#?

Based on this post:

http://help.mandrill.com/entries/23704122-Authenticating-webhook-requests

I have been trying to get something to work and my signature never matches the mandrill signature.

Was wondering if anyone else had managed this and had any example code?

Gus
Jan 14, 2014 at 12:34 AM
Edited Jan 14, 2014 at 12:36 AM
Hi. I know it's too late :)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Security.Cryptography;

namespace Mandrill
{
    public static class Extensions
    {
        public static void Iter<T>(this IEnumerable<T> xs, Action<T> action)
        {
            foreach (var x in xs) {
                action.Invoke(x);
            }
        }
    }

    public class MandrillAuth
    {
        private string GetPayload(HttpWebRequest request)
        {
            string data;

            // Read request payload
            using (var sr = new StreamReader(request.GetRequestStream()))
            {
                data = sr.ReadToEnd();
            }

            // Parse and combine request data
            var qs = System.Web.HttpUtility.ParseQueryString(data);
            var sb = new StringBuilder(request.RequestUri.ToString());
            qs.AllKeys.OrderBy(x => x).Iter(x => sb.Append(x).Append(qs[x]));

            return sb.ToString();
        }

        public bool Authenticate(string key, HttpWebRequest request)
        {
            // Cannot auth without key
            if (string.IsNullOrEmpty(key)) { return true; }

            // Check signature header
            if (!request.Headers.AllKeys.Contains("X-Mandrill-Signature")) { return false; }

            // Get request data
            string data = GetPayload(request);

            // Compare signature with computed hash
            using (var hashAlg = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
            {
                var hash = hashAlg.ComputeHash(Encoding.UTF8.GetBytes(data));
                string signature = request.Headers["X-Mandrill-Signature"];

                return signature == Convert.ToBase64String(hash);
            }
        }
    }
}
Feb 17, 2014 at 9:56 AM
Many thanks for responding. Yes, I ended up rolling my own. It looks like this:

I have this in web.config
<appSettings>
    <add key="url" value="http://10.219.123.217/MandrillListener/MandrillListener.aspx" />
    <add key="secretkey" value="mySecretKey" />
  </appSettings>
Then the following user control:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Listener.ascx.cs" Inherits="MyMandrillListener.ReceiveWebHook" %>
<div> Listener Control installed and waiting for Mandrill messages.. </div>
using System;
using System.Configuration;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.UI;

//  note: refer to this article for how to authenticate Mandrill web hooks
//  http://help.mandrill.com/entries/23704122-Authenticating-webhook-requests


namespace MyMandrillListener
{

    public partial class ReceiveWebHook : UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {

            // get the url from the web.config
            var url = ConfigurationManager.AppSettings["url"];
            var secretkey = ConfigurationManager.AppSettings["secretkey"];
           
            // get the hashed signature from the header
            var mandrillHeader = Request.Headers["X-Mandrill-Signature"];

            // get the form parameters sorted alphabetically and concatenated into a string with no delimiters
            var paramsString = GetSortedFormParams();
            url += paramsString; // add the list of sorted post key/value pairs to the url

            // now hash the string so we can compare it with the one from Mandrill
            var myHash = EncodeHmac(url, secretkey);

            if (mandrillHeader == myHash) // check if our hashed value matches the Mandrill one
            {
                // if we receive a Mandrill web hook message then get the json and save it to a file
                var mandrillEvents = Request["mandrill_events"];
                System.IO.File.AppendAllText(@"C:\Work\Mandrill\MandrillListener\MandrillHook.txt", mandrillEvents);
            }
            else
            {
                const string authenticationError = "Authentication Failed!";
                System.IO.File.AppendAllText(@"C:\Work\Mandrill\MandrillListener\MandrillHook.txt", authenticationError);
            }


        }

        private string GetSortedFormParams()
        {
            // Mandrill requires the posted parameters to be sorted alphabetically and appended to the url
            // and then hashed with hmac-sha1

            var postParams = (from string s in Request.Form.Keys select s + Request.Params[s] into formKey select formKey.Trim()).ToList();
            postParams.Sort(); 

            // get the form key/value pairs into a single long string without delimiters
            var paramsString = postParams.Aggregate("", (current, param) => current + param);
            return paramsString;

        }

        // this hashes our string using the Mandrill web hook key
        private static string EncodeHmac(string input, string key)
        {
            var encoding = new ASCIIEncoding();
            var hmac = new HMACSHA1(encoding.GetBytes(key));
            var stringBytes = Encoding.UTF8.GetBytes(input);
            var hashedValue = hmac.ComputeHash(stringBytes);
            return Convert.ToBase64String(hashedValue);
        }
    }
}
cheers
Gus