Last updated
In your authorization pipeline:
```
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var req = context.HttpContext.Request;
var authHeaders = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
if (authHeaders != null && authHeaders.StartsWith("hmacauth"))
{
//remove "hmacauth " from the start of the header
var rawAuthHeader = authHeaders.Substring(9);
var authorizationHeaderArray = GetAuthorizationHeaderValues(rawAuthHeader);
if (authorizationHeaderArray != null)
{
var companyId = authorizationHeaderArray[0];
var incomingBase64Signature = authorizationHeaderArray[1];
var nonce = authorizationHeaderArray[2];
var requestTimeStamp = authorizationHeaderArray[3];
var isValid = IsValidRequest(req, companyId, incomingBase64Signature, nonce, requestTimeStamp);
if (isValid.Result)
{
//handle valid request
}
else
{
//handle invalid request - hash did not match
}
}
else
{
//handle invalid request - missing/invalid header value
}
}
else
{
//handle invalid request - missing/invalid header value
}
return Task.FromResult(0);
}
```
Helper methods:
```
private string[] GetAuthorizationHeaderValues(string rawAuthHeader)
{
var credArray = rawAuthHeader.Split(':');
if (credArray.Length == 4)
{
return credArray;
}
else
{
return null;
}
}
private async Task<bool> IsValidRequest(HttpRequest req, string companyId, string incomingBase64Signature, string nonce, string requestTimeStamp)
{
string requestContentBase64String = "";
string requestUri = HttpUtility.UrlEncode(req.GetDisplayUrl().ToLower());
string requestHttpMethod = req.Method;
if (companyId != "YOUR GRAFFLE COMPANY ID")
{
return false;
}
var sharedKey = "YOUR BASE-64 ENCODED HMAC TOKEN";
if (IsReplayRequest(nonce, requestTimeStamp))
{
return false;
}
var contentStream = new StreamReader(req.Body);
byte[] hash = ComputeHash(await contentStream.ReadToEndAsync());
if (hash != null)
{
requestContentBase64String = Convert.ToBase64String(hash);
}
string data = String.Format("{0}{1}{2}{3}{4}{5}", companyId, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);
var secretKeyBytes = Convert.FromBase64String(sharedKey);
byte[] signature = Encoding.UTF8.GetBytes(data);
using (HMACSHA256 hmac = new HMACSHA256(secretKeyBytes))
{
byte[] signatureBytes = hmac.ComputeHash(signature);
var base64Signature = Convert.ToBase64String(signatureBytes);
return (incomingBase64Signature.Equals(base64Signature, StringComparison.Ordinal));
}
}
private bool IsReplayRequest(string nonce, string requestTimeStamp)
{
if (System.Runtime.Caching.MemoryCache.Default.Contains(nonce))
{
return true;
}
DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
TimeSpan currentTs = DateTime.UtcNow - epochStart;
var serverTotalSeconds = Convert.ToUInt64(currentTs.TotalSeconds);
var requestTotalSeconds = Convert.ToUInt64(requestTimeStamp);
if ((serverTotalSeconds - requestTotalSeconds) > requestMaxAgeInSeconds)
{
return true;
}
System.Runtime.Caching.MemoryCache.Default.Add(nonce, requestTimeStamp, DateTimeOffset.UtcNow.AddSeconds(requestMaxAgeInSeconds));
return false;
}
private static byte[] ComputeHash(string requestBody)
{
using (MD5 md5 = MD5.Create())
{
byte[] hash = null;
var content = Encoding.UTF8.GetBytes(requestBody);
if (content.Length != 0)
{
hash = md5.ComputeHash(content);
}
return hash;
}
}
```var crypto = require('crypto');
var body = '{"id":"8e16ee18d644974a00e8bb6aba16b23162dc81db17edd1108ced04191ec058e5","graffleProjectId":"6d2ff96e-715f-42fc-87f7-136da141bdde","graffleCompanyId":"29df57b8-a4ff-4ae9-bc9b-1fb50c49ac54","flowEventId":"A.c1e4f4f4c4257510.TopShotMarketV3.MomentListed","graffleEventToken":"59f6debd-128b-4f05-9e0f-d60e33eea21e","blockHeight":24824433,"eventDate":"2022-02-26T02:56:20.4927984+00:00","createdAt":"2022-02-26T02:56:22.6460387+00:00","metaData":null,"blockEventData":{"id":12204291,"price":4.00000000,"seller":"0xab8034319996ec49"},"webHook":"https://webhook.site/b2996651-a887-44ea-97e4-d2c1871e8a89","flowTransactionId":"d7f08a91f59e0aa6b411e9cc140180d1f2b2296c35b886b6414013acf43167e2"}';
console.log('body');
console.log(body);
var hash = crypto.createHash('md5').update(body).digest('base64');
console.log(hash);
var csharpHash = 'dxFH3n75au/c1cLSmB53AA==';
console.log('request body hash matches:');
console.log(hash === csharpHash);
var secret = 'dGVzdA==';
console.log('base64 secret:');
console.log(secret);
var secretbuff = new Buffer.from(secret, 'base64');
var secrettext = secretbuff.toString('utf8');
console.log('utf8 secret:');
console.log(secrettext);
var secretKeyBytes = [];
for (var i = 0; i < secrettext.length; i++) {
secretKeyBytes.push(secrettext.charCodeAt(i));
}
console.log('secret bytes:');
console.log(Buffer.from(secretKeyBytes));
var data = '29df57b8-a4ff-4ae9-bc9b-1fb50c49ac54POSThttps%3a%2f%2fwebhook.site%2fb2996651-a887-44ea-97e4-d2c1871e8a89164584420609ed04a357254562bd969530a2b295aedxFH3n75au/c1cLSmB53AA==';
var utf8Data = unescape(encodeURIComponent(data, 'utf-8'));
var signature = [];
for (var i = 0; i < utf8Data.length; i++) {
signature.push(utf8Data.charCodeAt(i));
}
console.log('signaturebytes:');
console.log(signature);
var base64Signature = crypto.createHmac('sha256', Buffer.from(secretKeyBytes)).update(Buffer.from(signature, 'utf8')).digest('base64');
console.log('base64Signature:');
console.log(base64Signature);
var incomingBase64Signature = 'zGa8YdMC2LE1Jo+8+fcIkrsNasM36OJ10eFkBhAGEdA='
console.log("IsValidRequest:")
console.log(incomingBase64Signature == base64Signature);