node.jsmqttiotgoogle-cloud-iot

Trouble Connecting to Google Cloud IoT via MQTT with Node.js


I'm trying to create a MQTT client that'll connect to the Google Cloud IoT Core, but for some reason, it won't connect at all. Here's what I have so far

mqtt = require("mqtt")
fs = require("fs")
var jwt = require('jsonwebtoken');


const projectId = "my-project" 
const deviceId = "my-device" 
const registryId = "my-degistry" 
const region = "us-central1"
const algorithm = "RS256"
const privateKeyFile = "./rsa_private.pem"
const mqttBridgeHostname = "mqtt.googleapis.com"
const mqttBridgePort = 8883
const messageType = "events"

//The mqttClientId is a unique string that identifies a particular device. 
//For Google Cloud IoT Core, it must be the format below
const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${deviceId}`
const mqttTopic = `/devices/${deviceId}/${messageType}`;


const createJwt = (projectId, privateKeyFile, algorithm) => {
    // Create a JWT to authenticate this device. The device will be disconnected
    // after the token expires, and will have to reconnect with a new token. The
    // audience field should always be set to the GCP project id.
    const token = {
      iat: parseInt(Date.now() / 1000),
      exp: parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
      aud: projectId,
    };
    const privateKey = fs.readFileSync(privateKeyFile);
    return jwt.sign(token, privateKey, {algorithm: algorithm});
  };

//Username field is ignored in Cloud IoT Core, but it must be set to something
//Password field sends a JWT (javascript web token) to authorize the device
//mqtts protocol causes library to connecti using SSL, which is required for IoT Core
const connectionArgs = {
    host: mqttBridgeHostname,
    port: mqttBridgePort,
    clientId: mqttClientId,
    username: "unused",
    password: createJwt(projectId, privateKeyFile, algorithm),
    protocol: "mqtts",
    secureProtocol: "TLSv1_2_method"
}

const client = mqtt.connect(connectionArgs)

client.on("connect", (connected)=>{
    console.log("Attempting to connect")
    if (!connected) {
        console.log("Client failed to connect")
    } else {
        console.log("Client is connected!")
    }
})

client.on("error", err => {
    console.log(err)
    setTimeout(( ()=> {  
        console.log('Terminating process')
        return process.kill(process.pid);
    }), 1000);
})

client.on("packetsend", (payload) => {
    console.log("Payload has been sent")
    return process.kill(process.pid)
})

client.on("packetreceive", packet => {
    console.log("Killing")
    //return process.kill(process.pid)
})

client.on("reconnect", ()=>{
    console.log("Attempting a reconnect")
    //return process.kill(process.pid)
})

client.on("close", ()=>{
    console.log("A disconnect occurred")
    // return process.kill(process.pid)
})

client.on("offline", () => {
    console.log("Client is offline")
    //return process.kill(process.pid)
})

I'm not getting any errors when I try to connect to the server. In other words, everything seems to be authenticating properly and I get no error messages, but the client never connects to the Cloud and instead repeatedly tries to reconnect in an endless cycle (which is why I included code to kill the script). I tried going through the Google Cloud troubleshooting page but nothing there really seemed to help. I don't get any sort of errors messages or helpful tidbits of information when using the Cloud SDK like the guide suggested.

I've opened up the port 8883 through my firewall just in case that was the issue but it doesn't appear to be.

I based this code off some of Google's guides and based on this guide here. I have a registry, project, and device all set up with a proper RSA key.

So I'm not really sure how to proceed! If there's any additional information that would help, please let me know.

Thank you.


Solution

  • Apart from the links I provided in the comment section and as additional to what you have found out, some users use the Project Number instead of the Project ID which leads to a similar concern which you have encountered. It really pays to double check everything in your configuration as you troubleshoot.

    If you need to have a refresher about the authentication, you may also refer to this link.