I've been trying to get the Log Collector API working in a node.js Azure Function but am stuck on the 403/Forbidden error which indicates I'm not forming the authorization header correctly. The complete code is in a github repository here:
https://github.com/sportsmgmt-labs/Azure-Log-Analytics-Node-Function
The Data Collector API documentation is here:
https://learn.microsoft.com/en-us/azure/log-analytics/log-analytics-data-collector-api
The authorization header should be formatted as follows:
Authorization: SharedKey {WorkspaceID}:{Signature}
Where the signature is encoded/encrypted like this:
Base64(HMAC-SHA256(UTF8(StringToSign)))
Here is my code that is creating the authorization header:
var contentLength = Buffer.byteLength(req.body['log-entry'], 'utf8');
var authorization = 'POST\n' + contentLength + '\napplication/json\nx-ms-date:' + processingDate + '\n/api/logs';
// encode string using Base64(HMAC-SHA256(UTF8(StringToSign)))
authorization = crypto.createHmac('sha256', sharedKey).update(authorization.toString('utf8')).digest('base64');
authorization = 'Authorization: SharedKey ' + workspaceId + ':' + authorization;
The response from the server is:
{"Error":"InvalidAuthorization","Message":"An invalid scheme was specified in the Authorization header"}
Could someone please help me understand what I'm doing wrong? Thanks!
Edit: Here is the Python code to do this:
def build_signature(customer_id, shared_key, date, content_length, method, content_type, resource):
x_headers = 'x-ms-date:' + date
string_to_hash = method + "\n" + str(content_length) + "\n" + content_type + "\n" + x_headers + "\n" + resource
bytes_to_hash = bytes(string_to_hash).encode('utf-8')
decoded_key = base64.b64decode(shared_key)
encoded_hash = base64.b64encode(hmac.new(decoded_key, bytes_to_hash, digestmod=hashlib.sha256).digest())
authorization = "SharedKey {}:{}".format(customer_id,encoded_hash)
return authorization
...and the C# code:
static void Main()
{
// Create a hash for the API signature
var datestring = DateTime.UtcNow.ToString("r");
string stringToHash = "POST\n" + json.Length + "\napplication/json\n" + "x-ms-date:" + datestring + "\n/api/logs";
string hashedString = BuildSignature(stringToHash, sharedKey);
string signature = "SharedKey " + customerId + ":" + hashedString;
PostData(signature, datestring, json);
}
// Build the API signature
public static string BuildSignature(string message, string secret)
{
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = Convert.FromBase64String(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hash = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hash);
}
}
You need to decode the shared key first. Please try changing the following lines of code:
authorization = crypto.createHmac('sha256', sharedKey).update(authorization.toString('utf8')).digest('base64');
authorization = 'Authorization: SharedKey ' + workspaceId + ':' + authorization;
to:
authorization = crypto.createHmac('sha256', new Buffer(sharedKey, 'base64')).update(authorization, 'utf-8').digest('base64');
var signature = 'SharedKey ' + workspaceId + ':' + authorization;
Then the request header will look like:
headers: {
'content-type': 'application/json',
'Authorization': signature,
'Log-Type': <log_type>,
'x-ms-date': processingDate
},
var request = require('request');
var crypto = require('crypto');
// Azure Log Analysis credentials
var workspaceId = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
var sharedKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
var apiVersion = '2016-04-01';
var processingDate = new Date().toUTCString();
var jsonData = [{
"slot_ID": 12345,
"ID": "5cdad72f-c848-4df0-8aaa-ffe033e75d57",
"availability_Value": 100,
"performance_Value": 6.954,
"measurement_Name": "last_one_hour",
"duration": 3600,
"warning_Threshold": 0,
"critical_Threshold": 0,
"IsActive": "true"
},
{
"slot_ID": 67890,
"ID": "b6bee458-fb65-492e-996d-61c4d7fbb942",
"availability_Value": 100,
"performance_Value": 3.379,
"measurement_Name": "last_one_hour",
"duration": 3600,
"warning_Threshold": 0,
"critical_Threshold": 0,
"IsActive": "false"
}]
var body = JSON.stringify(jsonData);
var contentLength = Buffer.byteLength(body, 'utf8');
var stringToSign = 'POST\n' + contentLength + '\napplication/json\nx-ms-date:' + processingDate + '\n/api/logs';
var signature = crypto.createHmac('sha256', new Buffer(sharedKey, 'base64')).update(stringToSign, 'utf-8').digest('base64');
var authorization = 'SharedKey ' + workspaceId + ':' + signature;
var headers = {
"content-type": "application/json",
"Authorization": authorization,
"Log-Type": 'WebMonitorTest',
"x-ms-date": processingDate
};
var url = 'https://' + workspaceId + '.ods.opinsights.azure.com/api/logs?api-version=' + apiVersion;
request.post({url: url, headers: headers, body: body}, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});