Reputation: 158
I have an C# MVC project to which I recently added WebApi. I've secured the API with HMAC Authentication and am using a CustomDelegatingHandler to add the authorization to the header of the request.
I have done this successfully on another project, using the same code, and it works. But on this new project, the Authentication only works once, then every other call to the API fails due to "Unauthorized". This only happens on our development/test server and not on my local machine running the project in Visual Studio 2017.
What might cause this odd behavior?
Here is the CustomDelegatingHandler I'm using:
public class CustomDelegatingHandler : DelegatingHandler
{
private string APPId = "";
private string APIKey = "";
public CustomDelegatingHandler(string appid, string apikey)
{
APPId = appid;
APIKey = apikey;
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
string requestContentBase64String = string.Empty;
string requestUri = System.Web.HttpUtility.UrlEncode(request.RequestUri.AbsoluteUri.ToLower());
string requestHttpMethod = request.Method.Method;
//Calculate UNIX time
DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
TimeSpan timeSpan = DateTime.UtcNow - epochStart;
string requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
//create random nonce for each request
string nonce = Guid.NewGuid().ToString("N");
//Checking if the request contains body, usually will be null wiht HTTP GET and DELETE
if (request.Content != null)
{
byte[] content = await request.Content.ReadAsByteArrayAsync();
MD5 md5 = MD5.Create();
//Hashing the request body, any change in request body will result in different hash, we'll incure message integrity
byte[] requestContentHash = md5.ComputeHash(content);
requestContentBase64String = Convert.ToBase64String(requestContentHash);
}
//Creating the raw signature string
string signatureRawData = String.Format("{0}{1}{2}{3}{4}{5}", APPId, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);
var secretKeyByteArray = Convert.FromBase64String(APIKey);
byte[] signature = Encoding.UTF8.GetBytes(signatureRawData);
using (HMACSHA256 hmac = new HMACSHA256(secretKeyByteArray))
{
byte[] signatureBytes = hmac.ComputeHash(signature);
string requestSignatureBase64String = Convert.ToBase64String(signatureBytes);
//Setting the values in the Authorization header using custom scheme (amx)
request.Headers.Authorization = new AuthenticationHeaderValue("amx", string.Format("{0}:{1}:{2}:{3}", APPId, requestSignatureBase64String, nonce, requestTimeStamp));
}
response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Upvotes: 0
Views: 1774
Reputation: 36
We had a similar issue which turned out to be related to the time stamp of the client vs. the server.
Solved it (server-side) by checking that the request was within the allowed time this way:
(a > b ? a-b : b-a) > maxtime
vs a - b > maxtime
If b > a, being an unsigned long (ulong), the value went to a huge value, thus triggering an invalid request condition.
1522896501 - 1522896502 > 300 (18446744073709551615 is what was calculated)
Upvotes: 2