Mqtt.js: How to change re-connect strategy to re-connect again with different credentials.

Created on 4 Feb 2018  路  14Comments  路  Source: mqttjs/MQTT.js

Hi

Is it possible to re-connect using different credentials
every time, instead of having to clear the old instances
of MQTT client? (which might be problem in terms of cleanup or memory leaks)

All 14 comments

If you're referring to altering the username/password you connect with, I'm having some success with manually changing the fields in the client's options field (e.g. client.options.password = newPassword;). However, this is undeniably a hack, and I'm hopping that there's another, better way to accomplish the same thing. For example, Client#reconnect accepts an options argument where you can pass new stores in. It would be nice if we could optionally pass an object containing options that we want to update.

Part of the reason that this method concerns me is that the initial options object is stored in the streamBuilder wrapper that the client calls to build a new connection. As such, if the options object is mutated, there's no guarantee that the options the connection implementation is using will match--which, as far as I can tell, doesn't matter for things like the username and password fields. However, "as far as I can tell" makes me nervous.

Fortunately, looking through the code, the streamBuilder (the wrapper the client calls to get a new connection) has the client passed to it when it's called--which means that the wrapper could just look at the client's options object instead of using the initial one.

Of course, I'm unfamiliar with the codebase, so there could easily be stuff I'm missing.

P.S. apologies if I screwed the terminology up--"connection"/"connection implementation" is probably the wrong word, but I couldn't think of anything better. I'm basically referring to the result of streamBuilder, which should hopefully be clear from context/the linked code.


On a side note, I should probably explain why I need to be able to update the password field. Google Cloud IoT Core requires that you send a signed JSON web token in the password field to authenticate the device. This token has a maximum lifetime of twenty-four hours (plus skew). This limits the usefulness of a stolen token, which seems (to my inexperienced self) rather reasonable.

However, this need to update the password field means that I either need to basically rebuild the client every time the token expires--which is, uh, non-optimal--or hack around with the options object, which seems to work, but makes me nervous that I'm going to run into an error later on down the road where I need to use a different protocol or modify a different field and everything's going to break in some really subtle and hard to diagnose way.

Would you like to make a proposal (PR) on how to improve the current situation?

Sure. I'm not sure how quickly I'll be able to get it done--I'm trying to catch up on a backlog of school work--and I've never actually made a pull request before, but I'll try to have something by Friday? Probably sooner, if I'm honest.

@mcollina where/how should I ask questions about what I should implement? For example, where should I ask if I should try to extract functionality related to loading-in options into a separate private method?

You open a PR and we discuss them.

Does that mean that I should open the PR before I have any changes made?

(Apologies for the silly/basic questions.)

@einargs I mean those changes are very hard to reason about without code that implements them. Propose some changes on how things should work, and we'll work from there.

@einargs Another option could be sending options as a function (that returns an object) rather than a object. It might be more flexible if other options also need changing. ie. expired username/password.

You could define a getter on the options object which returns different credentials every time. 馃槀

I also want to use this lib with AWS IoT.
The problem am facing is as followed.
My streamBuilder function returns a promise that will later resolve.
This is required to lazily refresh AWS IAM credentials and sign the URL.

const client = mqtt.MqttClient(function(){
  const credentials = await IAM.credentials();
  const signedUrl = ...
  return websocket(signedUrl, [ 'mqttv3.1' ]);
});

Can we get promise support for the streamBuilder parameter?

Hi ivands. I'd suggest synchronously returning a stream, then piping your websocket into it once your promise resolves.

Something like this:

  • Create two PassThrough streams, one for input, and one for output
  • Create a duplex stream using the two passthrough streams. I'm sure there's something on NPM for this
  • Create a promise to get credentials. Once it resolves, create the websocket stream, pipe the input into it, and pipe it into the output.
  • Return the duplex stream synchronously

That way you can have your actual stream get created asynchronously, without needing to change anything in the library.

Otherwise, if you want to add asynchronous support, it'll require changing _setupStream in the Client implementation to handle promises (or preferably node-style callbacks). I think a PR which adds unit tests and is backwards compatible would be welcome in that case.

Thanks @RangerMauve that solution works perfectly well.

@ivands would you mind sharing some relevant code snippets? I haven't worked with streams much so I'm still a bit lost.

this issue seems to be resolved with the documentation provided for transformWsUrl, so I will close this issue. Feel free to reopen.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DavidMG01 picture DavidMG01  路  4Comments

sueblue picture sueblue  路  6Comments

markcnunes picture markcnunes  路  7Comments

helxsz picture helxsz  路  3Comments

amerllica picture amerllica  路  3Comments