1
votes

I keep getting back Connection refused: Not authorized when trying to connect to the Azure IotHub using MQTT.js (https://github.com/mqttjs/MQTT.js).

The SAS password is generated using Microsoft Device Explorer (https://github.com/Azure/azure-iot-sdk-csharp/tree/master/tools/DeviceExplorer) so providing the right read/write connection details along with an expiry date in the future, I am assuming it is correct and valid.

I am also requiring TLS/SSL to be enabled (as per Microsoft Azure documentation: https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-mqtt-support#tlsssl-configuration) via rejectUnauthorized: false (as explained in the documentation for MQTT.js at https://github.com/mqttjs/MQTT.js#client).

How do I connect via Javascript using a 3rd party MQTT library and a SAS token?

This is a snippet of the Javascript code:

var MQTT = require("async-mqtt");

const deviceId = "<PUT_SOMETHING_HERE>";
const iotHubName = "<PUT_SOMETHING_HERE>";

const url = `${iotHubName}.azure-devices.net/${deviceId}/api-version=2016-11-14`;
const iotHubTopic = `devices/${deviceId}/messages/events/`

var client = MQTT.connect(`mqtts://${url}:8883`, {
  username: url,
  password: `SharedAccessSignature sr=${iotHubName}.azure-devices.net&sig=<COMBINATION_OF_PASSWORDS_URL_ENCODED>&se=<EPOCH_EXPIRY>&skn=<ACCESS_POLICY_NAME>`,
  rejectUnauthorized: false, // https://github.com/mqttjs/MQTT.js#client
});

// this is code from the MQTT.js example, but I don't even reach it
async function doStuff() {

    console.log("Starting");
    try {
        await client.publish(iotHubTopic, "It works!");
        // This line doesn't run until the server responds to the publish
        await client.end();
        // This line doesn't run until the client has disconnected without error
        console.log("Done");
    } catch (e){
        // Do something about it!
        console.log("Error while sending a message...");
        console.log(e.stack);
        process.exit();
    }
}

const ceremony = () => {
  return new Promise((resolve, reject) => {
      client.on("connect", doStuff);
      return resolve();
    })
    .then((stuff) => {
      console.log("Done?", stuff);
    })
    .catch((err) => {
      console.log("Err...", err);
      process.exit();
    });
}

ceremony();

The output is this:

Done? undefined
events.js:183
      throw er; // Unhandled 'error' event
      ^

Error: Connection refused: Not authorized
    at MqttClient._handleConnack (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/lib/client.js:896:15)
    at MqttClient._handlePacket (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/lib/client.js:332:12)
    at work (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/lib/client.js:274:12)
    at Writable.writable._write (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/lib/client.js:284:5)
    at doWrite (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:428:64)
    at writeOrBuffer (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:417:5)
    at Writable.write (<PATH_TO_THE_JS_PROJECT>/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:334:11)
    at TLSSocket.ondata (_stream_readable.js:639:20)
    at emitOne (events.js:116:13)
    at TLSSocket.emit (events.js:211:7)
2
Why not use the IoT Hub Device SDK? 3rd party MQTT libs are kind of a hit-and-miss with Azure IoT Hub, since it's not a real broker (at least it's not meant to be used like that).evilSnobu
Just tried it out and, indeed, it throws unauthenticated for MQTTS but works over Websockets, with var client = MQTT.connect(`wss://${url}`,...evilSnobu
Nope, spoke too soon. Hangs on wss://.evilSnobu
@evilSnobu I've a requirement to use MQTT (no AMQP or other protocols) and I was trying to avoid including code that I won't be using e.g. that IoT Hub SDK includes code for different protocols that I don't need. Also: plain MQTT is a valid option in the IoT Hub official documentation.TPPZ
Not necessarily. You could only import this package: npmjs.com/package/azure-iot-device-mqtt. For the Message type, you can just borrow the code from the azure-iot-common lib.evilSnobu

2 Answers

3
votes

Yes, Azure IoT Hub supports MQTT, you can use MQTT protocol to connect with the IoT Hub directly, and publish/subscribe the topics as send/receive messages with the documented topic names and topic filters. I have modified the above code, and it works fine.

var MQTT = require("async-mqtt");

const deviceId = "{the device name}";
const iotHubName = "{your iot hub name}";
const userName = `${iotHubName}.azure-devices.net/${deviceId}/api-version=2016-11-14`;
const iotHubTopic = `devices/${deviceId}/messages/events/`;

var client = MQTT.connect(`mqtts://${iotHubName}.azure-devices.net:8883`, {
  keepalive: 10,
  clientId: deviceId,
  protocolId: 'MQTT',
  clean: false,
  protocolVersion: 4,
  reconnectPeriod: 1000,
  connectTimeout: 30 * 1000,
  username: userName,
  password: "{SAS Token}",
  rejectUnauthorized: false, 
});

// this is code from the MQTT.js example, but I don't even reach it
async function doStuff() {

    console.log("Starting");
    try {
        await client.publish(iotHubTopic, "It works!");
        // This line doesn't run until the server responds to the publish
        await client.end();
        // This line doesn't run until the client has disconnected without error
        console.log("Done");
    } catch (e){
        // Do something about it!
        console.log("Error while sending a message...");
        console.log(e.stack);
        process.exit();
    }
}

const ceremony = () => {
  return new Promise((resolve, reject) => {
      client.on("connect", doStuff);
      return resolve();
    })
    .then((stuff) => {
      console.log("Done?", stuff);
    })
    .catch((err) => {
      console.log("Err...", err);
      process.exit();
    });
}

ceremony();

In the code, you need to note that:

  1. Use DEVICE ID in IoT Hub as clientId in the connection option;
  2. Use mqtts protocol;
  3. For the username option, use {iothubhostname}/{device_id}/api-version=2016-11-14, where {iothubhostname} is the full CName of the IoT hub.
  4. For the password field, use a SAS token. You can use Device Explorer to get the SAS token. The SAS token must be generated using a policy which involves the permissions: "registry write", "service connect" and "device connect" (a combination of less than these three might work, but providing "registry write" is not enough for sure).
0
votes

I was facing the same issue. After trying for half n hour, I came to know that one of my colleague had disabled my device on Iothub.😅