Search code examples
node.jsrestazureazure-storage

how do I construct a signature for a delete request which delete an entity in azure datastore in nodejs REST implementation?


I am implementing REST-API for deleting key/entity from table in azure datastore, but seems it has issue in authorization token, and i think there is issue in making 'stringTosign' i.e. 'Signature' in azure. Below are code,

const key = {
    "Namespace": "my-app",
    "Path": [
      {
        "Kind": 'tblxyz',
        "Name": "test-datastore-row"
      }
    ]
  };

REST Implementation:

                public async DeleteAPI(key: Key): Promise<any> {
                const tableService = await this.GetTableServiceAsync(); // gives me table metadata

                const url = "https://" + tableService.storageAccount + ".table.core.windows.net/" + 'tblxyz' + '(PartitionKey=' + '\'' + key.Namespace + '\'' + ',' + 'RowKey=' + '\'' + key.Path[0].Name + '\'' + ')';

                const timestamp = (new Date()).toUTCString();

                const stringToSign = timestamp + '\n/' +  tableService.storageAccount + '/' + 'tblxyz';
                const hmac = crypto.createHmac('sha256', new Buffer(tableService.storageAccessKey, 'base64'))
                  .update(stringToSign, 'utf-8')
                  .digest('base64');

                return new Promise((resolve, reject) => {
                  request.delete({
                    'headers': {
                      'Authorization': 'SharedKeyLite ' + tableService.storageAccount + ':' + hmac,
                      'x-ms-date': timestamp,
                      'x-ms-version': '2016-05-31',
                      'Content-Type': 'application/json',
                      'If-Match': '*'
                    },
                    'url': url,
                    'json': true
                  }, function (err, result) {
                    if (err) {
                      console.log('inside delete err', JSON.stringify(err));
                      return reject(err);
                    }
                    if (result.statusCode !== 200 && result.statusCode !== 204) {
                      console.log('inside delete err: 2', JSON.stringify(result));
                      return reject(result.body);
                    }
                    return resolve();
                  });
                });
            }

while making call to this API, i am getting below error,

enter image description here

Anybody have face this problem? Need help ...!!!!

Error message:

Error: the object {
"odata.error": {
"code": "AuthenticationFailed"
"message": {
  "lang": "en-US"
  "value": "Server failed to authenticate the request. Make sure the value 
   of Authorization header is formed correctly including the 
   signature.\nRequestId:df933tt7-0002-0004-41c3-782e5g000000\nTime:2017-12-
   19T12:16:37.5434074Z"
}
}
} was thrown, throw an Error :)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)

Solution

  • It seems there's an issue with your stringToSign. Based on the documentation, it should include resource's encoded URI path. Can you try with the following:

    const stringToSign = timestamp + '\n/' +  tableService.storageAccount + '/' + 'tblxyz' + '(PartitionKey=' + '\'' + key.Namespace + '\'' + ',' + 'RowKey=' + '\'' + key.Path[0].Name + '\'' + ')';
    

    UPDATE

    Please see this sample code. Essentially the resource's path should be URL encoded:

    const request = require("request");
    const crypto = require("crypto");
    const url = require('url');
    
    var accountName = "account-name";
    var accountKey = "account-key";
    var tableName = "table-name";
    var pk = "partition-key-value";
    var rk = "row-key-value";
    
    const encodedUriPath = tableName + '(PartitionKey=' + '\'' + pk + '\'' + ', ' + 'RowKey=' + '\'' + rk + '\'' + ')';
    const endpoint = "https://" + accountName + ".table.core.windows.net/" + encodedUriPath;
    const parsedUrl = url.parse(endpoint);
    const timestamp = (new Date()).toUTCString();
    
    console.log(url);
    console.log(timestamp);
    const stringToSign = timestamp + '\n/' +  accountName + parsedUrl.path;
    console.log('--------------------------------------');
    console.log(stringToSign);
    
    const hmac = crypto.createHmac('sha256', new Buffer(accountKey, 'base64'))
                      .update(stringToSign, 'utf-8')
                      .digest('base64');
    console.log('--------------------------------------');
    console.log(hmac);                
    
    request.delete({
        'headers': {
          'Authorization': 'SharedKeyLite ' + accountName + ':' + hmac,
          'x-ms-date': timestamp,
          'x-ms-version': '2016-05-31',
          'Content-Type': 'application/json',
          'If-Match': '*'
        },
        'url': endpoint,
        'json': true
        }, function (err, result) {
        if (err) {
          console.log('inside delete err', JSON.stringify(err));
    
        } else {
            console.log(JSON.stringify(result));
        }
    });