2

I am trying to generate a valid SAS token (shared access signatures) to connect to the Azure IoT Hub, but I keep getting the error Connection refused: Not authorized when using the token.

I know the way I am connecting is working because when using a SAS token generated with the Microsoft Device Explorer (https://github.com/Azure/azure-iot-sdk-csharp/tree/master/tools/DeviceExplorer) it works as expected.

To write the Javascript code I started from this documentation https://learn.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1 comparing with some working implementations:

What am I doing wrong in the following Javascript implementation? I think it may be related to the SHA256 signature combined with URL escaping the string but I am not sure how to test/verify this.

const crypto = require('crypto');

// on the Azure Portal:
// sharedAccessKeyName is the "policy" field
// sharedAccessKeyValue is the "primary key"

const sign = (iotHubName, expirtyDeltaInSecondsFromNow, sharedAccessKeyName, sharedAccessKeyValue) => {
  // URL encode the IoT Hub host
  const encodedSasUrl = encodeURI(`${iotHubName}.azure-devices.net`.toLowerCase());
  // calculate the expiry end datetime as an epoch
  const expiry = parseInt(Date.now() / 1000 + expirtyDeltaInSecondsFromNow);
  // combine the previous two pieces of information
  const stringToSign = `${encodedSasUrl}\n${expiry}`;
  // sign the string using the primary key (sharedAccessKeyValue) and SHA256
  const hmac = crypto.createHmac('sha256', sharedAccessKeyValue);
  hmac.update(stringToSign);
  const hash = hmac.digest('hex');
  // encode the signed hash to base64 (making sure it's URL escaped)
  const encodedSignature = hash.toString('base64');
  // put all together into the SAS token
  const sasToken = `SharedAccessSignature sig=${encodedSignature}&se=${expiry}&skn=${sharedAccessKeyName}&sr=${encodedSasUrl}`;
  console.log("sasToken:", sasToken);
  return sasToken;
}
TPPZ
  • 4,447
  • 10
  • 61
  • 106
  • Could you try this demo https://github.com/Azure-Samples/functions-node-sas-token/blob/master/GetSasToken-Node/index.js – Jayendran Aug 07 '18 at 15:02
  • That demo involves a dependency `azure = require('azure-storage');` that I would like to avoid. I am looking for a pure javascript/node.js solution as it is done with the examples in other programming languages. – TPPZ Aug 07 '18 at 15:04

1 Answers1

4

Here is an example of generating SAS token via node.js.

var generateSasToken = function(resourceUri, signingKey, policyName, expiresInMins) {
    resourceUri = encodeURIComponent(resourceUri);

    // Set expiration in seconds
    var expires = (Date.now() / 1000) + expiresInMins * 60;
    expires = Math.ceil(expires);
    var toSign = resourceUri + '\n' + expires;

    // Use crypto
    var hmac = crypto.createHmac('sha256', new Buffer(signingKey, 'base64'));
    hmac.update(toSign);
    var base64UriEncoded = encodeURIComponent(hmac.digest('base64'));

    // Construct autorization string
    var token = "SharedAccessSignature sr=" + resourceUri + "&sig="
    + base64UriEncoded + "&se=" + expires;
    if (policyName) token += "&skn="+policyName;
    return token;
};

What am I doing wrong in the following Javascript implementation?

I am not an expert on javascript(node.js). Maybe you can figure it out via comparing above code and yours.

Narayana
  • 2,654
  • 3
  • 32
  • 32
Rita Han
  • 9,574
  • 1
  • 11
  • 24
  • 1
    Thanks that worked :) I think I was messing with the difference between providing a `string` instead of the bytes ( `new Buffer(...)` ) representing that string when signing it using SHA256. Also when converting the signature to a digest I was using `hex` instead of `base64`. – TPPZ Aug 08 '18 at 09:12
  • @TPPZ Glad to help. – Rita Han Aug 08 '18 at 09:30