How to create ConsumerGroups for Eventhub programatically using nodejs in Azure? - node.js

How to create consumergroups in eventhub using nodejs in Azure?
I tried to replicate what .net SDK offers, it did not work.
const { NamespaceManager } = require("#azure/service-bus");
let namespaceManager = NamespaceManager.CreateFromConnectionString(eventHubConnectionString);
let ehd = namespaceManager.GetEventHub(eventHubPath);
namespaceManager.CreateConsumerGroupIfNotExists(ehd.Path, consumerGroupName);

Here is the process that worked:
https://learn.microsoft.com/en-us/rest/api/eventhub/create-consumer-group
Steps:
Create SAS Token
Supply the right headers and make a https call to REST api
You can create it only once, if you call for the second time, it will throw an 409 error. If you want an update or insert call, you need to check for it.
SAS Token:
https://learn.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
uri -- url of your eventhub
saName -- Name of your Managed Policy
saKey -- Primary / Secondary Key of your EventHub Manage Policy (Ensure it has the Manage)
function createSharedAccessToken(uri, saName, saKey) {
if (!uri || !saName || !saKey) {
throw "Missing required parameter";
}
var encoded = encodeURIComponent(uri);
var now = new Date();
var week = 60*60*24*7;
var ttl = Math.round(now.getTime() / 1000) + week;
var signature = encoded + '\n' + ttl;
var signatureUTF8 = utf8.encode(signature);
var hash = crypto.createHmac('sha256', saKey).update(signatureUTF8).digest('base64');
return 'SharedAccessSignature sr=' + encoded + '&sig=' +
encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName;
}
Request Parameters:
URL:
https://your-namespace.servicebus.windows.net/your-event-hub/consumergroups/testCG?timeout=60&api-version=2014-01
Headers:
Content-Type: application/atom+xml;type=entry;charset=utf-8
Host: your-namespace.servicebus.windows.net
Authorization: {replace with the content from your SAS Token}
Payload:
<entry xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<ConsumerGroupDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">Any name you want</ConsumerGroupDescription>
</content>
</entry>
Possible Return Statuses:
201 -- Successful Creation
404 -- Not found, you are using a name that does not exist
409 -- The messaging entity 'XXX' already exists.
If you notice any other issues, please leave a comment.

Event hubs Node.JS SDK doesn't support management operations.
Try a management client like https://www.nuget.org/packages/Microsoft.Azure.Management.EventHub/

Related

Post Go Live issue with Docusign using node.js

Here is my issue:
We integrated docusign in our application, server side with nodejs using this tutorial https://github.com/docusign/docusign-node-client ("OAuth JSON Web Token (JWT) Grant" section)
We have done the "Go Live Process": our application is registered in our production account
We have replaced the test config to the production config.
When we try to create an envelope, we get the following error:
PARTNER_AUTHENTICATION_FAILED: The specified Integrator Key was not found or is disabled. Invalid account specified for user
What am I doing wrong ?
async function docusignInit() {
var options;
var env = [40077,50077].indexOf(config.main.port) != -1 ? 'test' :'prod';
if (env == "test") {
options = {
basePath: restApi.BasePath.DEMO,
oAuthBasePath: oAuth.BasePath.DEMO
}
} else {
options = {
oAuthBasePath: "account.docusign.com",
// We called https://account.docusign.com/oauth/userinfo to found the uri
basePath:"https://eu.docusign.net/restapi/"
}
}
// in production, We must do
// var apiClient = new docusign.ApiClient(options.basePath);
// Otherwise, we get "Error: getaddrinfo ENOTFOUND undefined undefined:443"
var apiClient = new docusign.ApiClient(options.basePath);
var privateKeyFile = fs.readFileSync(`./server/docusign/keys/${env}/private.PEM`);
var res = await apiClient.requestJWTUserToken(config.docusign.integratorKey, config.docusign.userName, [oAuth.Scope.IMPERSONATION, oAuth.Scope.SIGNATURE], privateKeyFile, 3600)
var token = res.body.access_token;
apiClient.addDefaultHeader('Authorization', 'Bearer ' + token);
docusign.Configuration.default.setDefaultApiClient(apiClient);
await sendDocusign({
userId: 1,
firstName: 'foor',
lastName: 'bar',
email:'foo#bar;'
})
}
async function sendDocusign(role) {
var envDef = new docusign.EnvelopeDefinition();
envDef.emailSubject = 'Please signe this';
envDef.templateId = config.docusign.templateId;
var role = new docusign.TemplateRole();
role.roleName = "roleName";
role.clientUserId = role.userId;
role.name = role.firstName + " " + role.lastName;
role.email = role.email;
envDef.allowReassign = false;
envDef.templateRoles = [role];
envDef.status = 'sent';
var envelopesApi = new docusign.EnvelopesApi();
return await envelopesApi.createEnvelope(config.docusign.userAccountId, {
'envelopeDefinition': envDef
})
}
As you are able to generate AccesToken properly in PROD with PROD RSA KeyPair, so please check the endpoint which you using to make API calls to create an envelope. In demo it is always demo.docusign.net but in PROD it will be a different value depending on where you PROD account exists in the DocuSign data center. For instance if your PROD account is in NA1, then hostname will be will be www.docusign.net; if it is NA2 then hostname will be na2.docusign.net etc.
So it is recommended to make a /userinfo API call with the Access token to know the baseURI to make calls related to envelope. To get the base URI, call the /oauth/userinfo endpoint, supplying your application’s access token as a header.
For the developer sandbox environment, the URI is
https://account-d.docusign.com/oauth/userinfo
For the production environment, the URI is
https://account.docusign.com/oauth/userinfo
Documentation related to /userinfo API call is available here. Once you know you BaseURI then append this baseURI with envelopes related endpoint like below:
{base_uri} + "/restapi/v2.1/accounts/" + {account_id}
considering your error seems that you're missing the integratorKey or you're writing it in the wrontg way. According to that LINK is possible that you miss the brackets inside the intregrator key?
The integrator key must be placed in front of the user ID that is in
the Username node of the UsernameToken. The integrator key must be
wrapped with brackets, “[ and ]”.
An example of the api in the above documentation:
<soap:Header>
<wsa:Action>http://www.docusign.net/API/3.0/GetRecipientEsignList</wsa:Action>
<wsa:MessageID>uuid:3f9d7626-c088-43b4-b579-2bd5e8026b17</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://demo.docusign.net/api/3.0/api.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-8838aa24-9759-4f85-8bf2-26539e14f750">
<wsu:Created>2006-04-14T14:29:23Z</wsu:Created>
<wsu:Expires>2006-04-14T14:34:23Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-7c7b695e-cef7-463b-b05a-9e133ea43c41">
<wsse:Username>[Integrator Key Here]2988541c-4ec7-4245-b520-f2d324062ca3</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
<wsse:Nonce>SjlScsL5q3cC1CDWrcMx3A==</wsse:Nonce>
<wsu:Created>2006-04-14T14:29:23Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>

How do I Get the Token I Need to for my Script to hit my Azure Stored Procedure?

I have an azure stored procedure, and I need to hit it with a python script that I'm going to upload as a webjob to schedule it to run once per day.
I've been reading the docs on executing a stored procedure, the common request headers for Azure Cosmos DB rest calls, and the page on access control, but the access control page mentions that these keys are for read queries only (so I assume not for hitting stored procedures, which have rights to do any sort of query or else that seems like a huge vulnerability hole).
I need to know specifically how do I get a key from Azure in python to hit my stored procedure endpoint?
Update 1
I was able, finally, to construct the Authorization string and send it, along with some other headers, to the server. But I am still getting an unauthorized response.
The response:
{
"code": "Unauthorized",
"message": "The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'post\nsprocs\ndbs/metrics/colls/LoungeVisits/sprocs/calculateAverage\nfri, 05 oct 2018 19:06:17 gmt\n\n'\r\nActivityId: 41cd36af-ad0e-40c3-84c8-761ebd14bf6d, Microsoft.Azure.Documents.Common/2.1.0.0"
}
The request headers:
{
Authorization: [my-auth-string],
x-ms-version: "2017-02-22", //My DB was created after this, the latest version, so I assume it uses this version; can I verify this somehow?
x-ms-date: "Fri, 05 Oct 2018 19:06:17 GMT", // My js for returning the auth string also returns the date, so I copy both in
Content-Type: application/json
}
Code to generate auth string which is then copy/pasted into Postman:
var crypto = require("crypto");
var inputKey = "my-key-from-azure";
var today = new Date().toUTCString();
console.log(today);
console.log(getAuthorizationTokenUsingMasterKey("POST", "dbs", "dbs/ToDoList", today, inputKey))
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceId, date, masterKey)
{
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
The page about authorization headers is for any Cosmos DB REST request: query, stored procedures, etc.
Azure cosmos DB has python SDK which is the recommended and supported way for such scenarios.
Also python SDK code is open-sourced. Here is the reference to auth header creation code enter link description here

Use nlapiRequestURL to make a request to a Service

How do you use nlapiRequestURL to make a request to a service? My attempt below is failing with the error: UNEXPECTED_ERROR (output from NetSuites script execution log).
My service is set to run without login and works correctly when I directly access it through a browser using its url. Its just the request through nlapiRequestURL thats failing.
Any idea what could be going wrong?
// This code executes in Account.Model.js (register function)
// I am using my own netsuite user credential here
var cred = {
email: "MY_NETSUITE_EMAIL"
, account: "EXXXXX" // My account id
, role: "3" // Administrator
, password: "MY_NETSUITE_PASSWORD"
};
var headers = {"User-Agent-x": "SuiteScript-Call",
"Authorization": "NLAuth nlauth_account=" + cred.account + ", nlauth_email=" + cred.email +
", nlauth_signature= " + cred.password + ", nlauth_role=" + cred.role,
"Content-Type": "application/json"};
var payload = {
type: 'is_email_valid'
, email: 'spt015#foo.com'
};
// A raw request to the service works fine:
// http://mywebsite.com/services/foo.ss?type=is_email_valid&email=spt015#foo.com
// Error occurs on next line
var response = nlapiRequestURL(url, payload, headers);
You are attempting to call a non-Netsuite url with Netsuite authentication headers. You do not need that unless for some reason of your own you have implemented NS-style authorization on your service.
nlapiRequestURL does not automatically format a payload into a query string. If your service takes a posted JSON body then you need to call JSON.stringify(payload) e.g
var response = nlapiRequestURL(url, JSON.stringify(payload), headers);
If your service needs a query string like in your example then you need to construct a query string and append it to your service url. e.g.
var qs = '';
for(var k in payload) qs += k +'='+ uriEncodeComponent(payload[k]) +'&';
var response = nlapRequestURL(url +'?'+ qs.slice(0,-1), null, headers);
I would suggest changing your nlapiRequestURL to a GET instead of POST, and add the parameters to the url instead. Your function call will look like this instead.
nlapiRequestURL(url, null, headers, "GET")

Create and Update Named Caches in Azure Managed Cache using Management API

I am attempting to create an Azure Managed Cache using PowerShell and the Azure Management API, this two pronged approach is required because the Offical Azure PowerShell Cmdlets only have very limited support for Creation and Update of Azure Managed Cache. There is however an established pattern for calling the Azure Management API from PowerShell.
My attempts at finding the correct API to call have been somewhat hampered by limited documentation on the Azure Managed Cache API. However after working my way through the cmdlets using both the source code and the -Debug option in PowerShell I have been able to find what appear to be the correct API endpoints, as such I have developed some code to access these endpoints.
However, I have become stuck after the PUT request has been accepted to the Azure API as subsequent calls to the Management API /operations endpoint show that the result of this Operation was Internal Server Error.
I have been using Joseph Alabarhari's LinqPad to explore the API as it allows me to rapidly itterate on a solution using the minimum possible code, so to execute the following code snippets you will need both LinqPad and the following extension in your My Extensions script:
public static X509Certificate2 GetCertificate(this StoreLocation storeLocation, string thumbprint) {
var certificateStore = new X509Store(StoreName.My, storeLocation);
certificateStore.Open(OpenFlags.ReadOnly);
var certificates = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
return certificates[0];
}
The complete source code including the includes are available below:
My Extensions - you can replace an "My Extensions" by right clicking My Extensions in the bottom left hand pane and choosing "Open Script Location in Windows Explorer" then replacing the highlighted file with this one. Alternatively you may wish to merge my extensions into your own.
Azure Managed Cache Script - you should simply be able to download and double click this, once open and the above extensions and certificates are in place you will be able to execute the script.
The following settings are used throughout the script, the following variables will need to it for anyone who is following along using their own Azure Subscription ID and Management Certificate:
var cacheName = "amc551aee";
var subscriptionId = "{{YOUR_SUBSCRIPTION_ID}}";
var certThumbprint = "{{YOUR_MANAGEMENT_CERTIFICATE_THUMBPRINT}}";
var endpoint = "management.core.windows.net";
var putPayloadXml = #"{{PATH_TO_PUT_PAYLOAD}}\cloudService.xml"
First I have done some setup on the HttpClient:
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(StoreLocation.CurrentUser.GetCertificate(certThumbprint));
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("x-ms-version", "2012-08-01");
This configures HttpClient to both use a Client Certificate and the x-ms-version header, the first call to the API fetches the existing CloudService that contains the Azure Managed Cache. Please note this is using an otherwise empty Azure Subscription.
var getResult = client.GetAsync("https://" + endpoint + "/" + subscriptionId + "/CloudServices");
getResult.Result.Dump("GET " + getResult.Result.RequestMessage.RequestUri);
This request is successful as it returns StatusCode: 200, ReasonPhrase: 'OK', I then parse some key information out of the request: the CloudService Name, the Cache Name and the Cache ETag:
var cacheDataReader = new XmlTextReader(getResult.Result.Content.ReadAsStreamAsync().Result);
var cacheData = XDocument.Load(cacheDataReader);
var ns = cacheData.Root.GetDefaultNamespace();
var nsManager = new XmlNamespaceManager(cacheDataReader.NameTable);
nsManager.AddNamespace("wa", "http://schemas.microsoft.com/windowsazure");
var cloudServices = cacheData.Root.Elements(ns + "CloudService");
var serviceName = String.Empty;
var ETag = String.Empty;
foreach (var cloudService in cloudServices) {
if (cloudService.XPathSelectElements("//wa:CloudService/wa:Resources/wa:Resource/wa:Name", nsManager).Select(x => x.Value).Contains(cacheName)) {
serviceName = cloudService.XPathSelectElement("//wa:CloudService/wa:Name", nsManager).Value;
ETag = cloudService.XPathSelectElement("//wa:CloudService/wa:Resources/wa:Resource/wa:ETag", nsManager).Value;
}
}
I have pre-created a XML file that contains the payload of the following PUT request:
<Resource xmlns="http://schemas.microsoft.com/windowsazure">
<IntrinsicSettings>
<CacheServiceInput xmlns="">
<SkuType>Standard</SkuType>
<Location>North Europe</Location>
<SkuCount>1</SkuCount>
<ServiceVersion>1.3.0</ServiceVersion>
<ObjectSizeInBytes>1024</ObjectSizeInBytes>
<NamedCaches>
<NamedCache>
<CacheName>default</CacheName>
<NotificationsEnabled>false</NotificationsEnabled>
<HighAvailabilityEnabled>false</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
</NamedCache>
<NamedCache>
<CacheName>richard</CacheName>
<NotificationsEnabled>true</NotificationsEnabled>
<HighAvailabilityEnabled>true</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
</NamedCache>
</NamedCaches>
</CacheServiceInput>
</IntrinsicSettings>
</Resource>
I construcuct a HttpRequestMessage with the above Payload and a URL comprised of the CloudService and Cache Names:
var resourceUrl = "https://" + endpoint + "/" + subscriptionId + "/cloudservices/" + serviceName + "/resources/cacheservice/Caching/" + cacheName;
var data = File.ReadAllText(putPayloadXml);
XDocument.Parse(data).Dump("Payload");
var message = new HttpRequestMessage(HttpMethod.Put, resourceUrl);
message.Headers.TryAddWithoutValidation("If-Match", ETag);
message.Content = new StringContent(data, Encoding.UTF8, "application/xml");
var putResult = client.SendAsync(message);
putResult.Result.Dump("PUT " + putResult.Result.RequestMessage.RequestUri);
putResult.Result.Content.ReadAsStringAsync().Result.Dump("Content " + putResult.Result.RequestMessage.RequestUri);
This request is nominally accepted by the Azure Service Management API as it returns a StatusCode: 202, ReasonPhrase: 'Accepted' response; this essentially means that the payload has been accepted and will be processed offline, the Operation ID can be parsed out of the HTTP Header to retreve further information:
var requestId = putResult.Result.Headers.GetValues("x-ms-request-id").FirstOrDefault();
This requestId can be used to request an update upon the status of the operation:
var operation = client.GetAsync("https://" + endpoint + "/" + subscriptionId + "/operations/" + requestId);
operation.Result.Dump(requestId);
XDocument.Load(operation.Result.Content.ReadAsStreamAsync().Result).Dump("Operation " + requestId);
The request to the /operations endpoint results in the following payload:
<Operation xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ID>5364614d-4d82-0f14-be41-175b3b85b480</ID>
<Status>Failed</Status>
<HttpStatusCode>500</HttpStatusCode>
<Error>
<Code>InternalError</Code>
<Message>The server encountered an internal error. Please retry the request.</Message>
</Error>
</Operation>
And this is where I am stuck, the chances are I am subtly malforming the request in such a way that the underlying request is throwing a 500 Internal Server Error, however without a more detailed error message or API documentation I don't think there is anywhere I can go with this.
We worked with Richard offline and the following XML payload got him un-blocked.
Note - When adding/removing named cache to an existing cache, the object size is fixed.
Note 2- The Azure Managed Cache API is sensitive to whitespace between the element and the element.
Also please note, we are working on adding Named cache capability to our PowerShell itself, so folks don't have to use APIs to do so.
<Resource xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<IntrinsicSettings><CacheServiceInput xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SkuType>Standard</SkuType>
<Location>North Europe</Location>
<SkuCount>1</SkuCount>
<ServiceVersion>1.3.0</ServiceVersion>
<ObjectSizeInBytes>1024</ObjectSizeInBytes>
<NamedCaches>
<NamedCache>
<CacheName>default</CacheName>
<NotificationsEnabled>false</NotificationsEnabled>
<HighAvailabilityEnabled>false</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
<ExpirationSettings>
<TimeToLiveInMinutes>10</TimeToLiveInMinutes>
<Type>Absolute</Type>
</ExpirationSettings>
</NamedCache>
<NamedCache>
<CacheName>richard</CacheName>
<NotificationsEnabled>false</NotificationsEnabled>
<HighAvailabilityEnabled>false</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
<ExpirationSettings>
<TimeToLiveInMinutes>10</TimeToLiveInMinutes>
<Type>Absolute</Type>
</ExpirationSettings>
</NamedCache>
</NamedCaches>
</CacheServiceInput>
</IntrinsicSettings>
</Resource>

Azure table storage query fails with AuthenticationFailed error in windows store application

azure table query rest api is failing with AuthenticationFailed error.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>AuthenticationFailed</code>
<message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.</message>
</error>
The winjs app code snippet to form and make the rest call is:
var date = new Date().toGMTString().replace('UTC', 'GMT');
var xhrOption = {
type: 'GET',
url: url,
headers: {
'content-type': 'application/atom+xml;charset="utf-8"',
'content-length': 0,
dataserviceversion: '1.0;NetFx',
maxdataserviceversion: '2.0;NetFx',
'x-ms-version': '2011-08-18',
'x-ms-date': date,
accept: 'application/atom+xml,application/xml',
'Accept-Charset': 'UTF-8',
},
};
xhrOption.headers.Authorization = AuthorizationHeader().computeForTableService(options, xhrOption);
The code to compute the authorization header is little long. It is listed below:
_getSignatureStringForTableService: function getSignatureStringForTableService()
{
var headers = this.xhrOptions.headers;
var httpVerb = this.xhrOptions.type.toUpperCase();
var sigItems = [];
sigItems.push(httpVerb);
var contentMD5 = this._getHeaderOrDefault(headers, 'Content-MD5');
sigItems.push(contentMD5);
var contentType = this._getHeaderOrDefault(headers, 'content-type');
sigItems.push(contentType);
var date = this._getHeaderOrDefault(headers, 'x-ms-date');
if (!date)
date = this._getHeaderOrDefault(headers, 'Date');
sigItems.push(date);
var canonicalizedResource = this._getCanonicalizedResource();
sigItems.push(canonicalizedResource);
var result = sigItems.join('\n');
return result;
},
_getCanonicalizedResource: function getCanonicalizedResource()
{
var items = [];
var path;
if (config.storageAccount.isDevStorage)
path = "/" + config.storageAccount.name + '/' + config.storageAccount.name;
else
path = "/" + config.storageAccount.name;
path += "/" + this.options.resourcePath;
items.push(path);
var result = items.join('\n');
return result;
},
computeForTableService: function computeForTableService(options, xhrOptions)
{
this.options = options;
this.xhrOptions = xhrOptions;
var sig = this._computeSignatureForTableService();
var result = 'SharedKey ' + config.storageAccount.name + ':' + sig;
return result;
},
_computeSignatureForTableService: function computeSignatureForTableService()
{
var sigString = this._getSignatureStringForTableService();
// TODO: use crypto from windows api. currently uses, google cryptoJS lib
var key = CryptoJS.enc.Base64.parse(config.storageAccount.primaryKey);
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac.update(sigString);
var hash = hmac.finalize();
var result = hash.toString(CryptoJS.enc.Base64);
return result;
},
Interestingly, I have the whole code working fine 2 days before. I have updated service code to use updated azure nodejs sdk. I wonder if the update caused some incompat in the publisher/consumer code?
Other observations
The service code that uses azure nodejs module, is able to query the table storage without error.
I debugged through the azure nodejs module, looked through the stringToSign and matched with what winjs code is producing. both are same afaik.
service was upgrade to use 0.10.x node and respective latest azure nodejs sdk.
Example: stringToSign
GET\n\napplication/atom+xml;charset="utf-8"\nWed, 5 Jun 2013 14:43:30 GMT\n/devstoreaccount1/devstoreaccount1/mytable()
Thanks for going through details.
Finally - the root cause of the bug is out. The issue is value of x-ms-date header.
Expected value - Thu, 06 Jun 2013 08:09:50 GMT
Value computed in the code above - Thu, 6 Jun 2013 08:20:34 GMT
The 0 missing before the date is the root cause of this bug. Because of that, stringToSign used in computing the authorization header is incorrect. Hence, Authorization Header is incorrect leading to AuthenticationFailed error. This also explains the reason why this code worked couple of days back (end of may - date had two digits).
If someone from MS is reading this, it will be so much useful to have right amount of details along with the error code. AuthenticationFailed error code alone does not give any clue to developer.
I had used azure storage blob rest api earlier. It returns better error for the same AuthenticationFailed error code. It sends across the expected stringToSign and found stringToSign along with the AuthenticationFailed error code. It is so much more helpful and bug gets resolved in couple of minutes.
Used Network monitor from Microsoft. Wrote c# code snippet to make the azure table query using azure .net sdk, and compared every header character by character to hit the issue.

Resources