How to get http headers from node-red-contrib-mdashboard

Hi

Is it possible to add one more field to msg beside socketid? For example, add authentication information such as userinfo to identify the authenticated user. (authentication user is another topic and just assumed userinfo is ready).

Thanks
Peter

Hi, I'm not sure the author of that node hangs out here very often. You may need to contact them direct via that nodes github issue page.

If you do contact the author in regard to user authentication, I'd love to be involved since I'm working on adding websocket based authentication and authorisation to uibuilder at the moment.

It would be great to agree on a standard data structure for the data.

I already have something that I'm using but nothing is published yet. You can see it by looking on the uibuilder GitHub site and switching to the "security" branch. The CHANGELOG and the docs folder has info.

You are right. I am talking about authentication. However, I believe that we only need to pass a specific header to nodes in the flow and it will work.

Should not force any authentication methods because there are many different ways to provide authentication to the flows and nodes. For my case, I use external identity providers. After authentication provided by API gateways, API gateways can pass necessary user information as http header.

OK, maybe a slight misunderstanding and some clarity on terms.

You mentioned adding to the msg which wouldn't adding a header.

When trying to secure web apps that are websocket driven such as Dashboard, mdashboard and uibuilder, HTTP headers only really secure the web pages themselves and cannot fully secure the websocket communications as they only present additional headers during the initial handshake.

So to properly secure websockets, you need to pass data in the msg as you said. My proposal is to have an agreed format for the information you might want to transfer in order to facilitate a login or subsequent security checks (e.g. continuing to exchange a JWT token).

I am currently adding a _uibAuth object property to the msg.

{
   "payload": ... ,
   "topic": ... ,
   "_msgId": ... ,
   "_socketId": ... ,

   "_uibAuth": {
      "id": ...unique user identifier... ,
      // Other potential information depending on need.
      // e.g. for a login:
      "password": ...encoded password... ,
      // or for ongoing session management:
      "jwt": ... JWT token (base 64 encoded) ... 
   }
}

If others wanted to pick up the same kind of schema, _uibAuth could be renamed to perhaps just _auth. But I didn't want to assume that if only uibuilder would be using it.

The same msg property is also used when sending information from the Node-RED server to the client as well. For example, on a successful login, you might return a message for the user from the server such as "Welcome to xxxxx, please remember to change your password" or whatever, you might also pass back other "meta-data" such as a timestamp when the users subscription expires.

Not of this implies any kind of fixed data or process other than using a standard msg property and using a standard id property within that. It is just a convention similar to the other msg conventions that Node-RED already uses (topic and payload for example).

Thanks for your information. However, my original request is about how to retrieve one specific http header (like id_token) value and set it as a property of msg. It is because I already have id_token in the http header for each request(websocket). But have no idea how to get this header out and set to msg property. I still don't know how to do that after reading your reply :slight_smile:

Anyway, I have solved this problem by using query parameter to pass the id_token without changing any existing code of mdashboard.

1 Like

I would be very surprised if you did because websockets doesn't support custom HTTP headers. As I said, only the initial handshake allows extra headers. If the handshake - which happens over standard http - then successfully upgrades to a websocket, only a limited subset of standard headers are passed.

This means, for example, that standard session expiry processes and JWT do not work as expected with websockets.

The only way to exchange custom information over websockets reliably is to pass the data in the msg not the header.

My reply was really about collaborating with other ui node authors. Sorry for the confusion.

Unless any of the nodes that you are using expose the HTTP res (response) object on the msg, I don't believe that you can get a header and so add it to a msg property. That would have to be the node's author as indicated by Dave. My previous comments we more about trying to ensure that nodes with similar aims add data to the msg object at source in a consistent way.

The response object is normally provided by the ExpressJS web server that Node-RED uses for web interactions. Depending on the implementation of the websocket library, the author may have access to some of that data from the initial handshake but it would only be available either when a user (re)loaded the page or when the websocket connection is first made. That may be enough for your purposes perhaps.

I think you are right. I can print all standard handshake headers. But handshake headers don't include my customized header.

I believe the only way is to pass id_token over query parameter.

Well that isn't a good solution either and it would still probably need changes in the nodes?

The reason that isn't good is that query parameters can be cached both in-browser and by intermediate caches and proxies. It isn't good practice to pass authentication or authorisation information via query parameters.

That's why I'm going to such pains with uibuilder to put the authentication, authorisation and session data into a standard object on the msg.

I'm also working to enforce the use of HTTPS (and WSS for websockets) - e.g. TLS encryption on the wire - when using security so that the msg's cannot be easily intercepted or decoded.

You are right. query also have problem. I need to find another way to work around this problem:

Is it possible to pass back socketid as one handshake response header (like "socketid")? How to do that and where is the code? in middleware function? how?

thanks

Not sure I understand - you are already getting _socketId aren't you?

I don't use the raw websocket front-end library in my code, I use Socket.IO which is more feature-rich. That does let you set a custom header for the initial handshake.

The problem you have is that, in using a Node like mdashboard, as with Dashboard, you are very limited in what you can do to customise how things work. That's why uibuilder exists at all. It lets you do all sorts of strange and customised things but at the cost of losing the really easy UI bits which you now have to do more of yourself.

I finally solved this problem. It is not too hard:

Send id_token as http header to node red as a handshake http header. This is doable and I already tested.

Once the id_token with its value received by node red, I modify mdashboard to save key value pair of (socket id - > id_token) as global variable.

For each flow node, if it requires to check user info, just get the id_token by providing socket id.

OK, sounds similar to part of what I'm doing for uibuilder. Except that I will hand off the session management to an external function so that users have control for themselves. That will let them build their own db or link into existing session store as needed. I'll provide a basic template. There will also be external functions to allow login/user validation processing.

However, in addition, I'll be adding dynamic session validation. Which is to say that once logged in, the client will receive a JWT token which it will automatically send back to the server on each message sent, the server will reject messages without it & will validate it. The admin can set a timeout for sessions so that the server forcibly logs out the client if the session expires. There will also be an option to auto-extend the session which will be based simply on the ping messages from the client and using the _sessionId rather than the JWT since it happens every 30sec so validating the JWT each time is probably too much. Obviously, a full message from the client with the JWT will also extend the session by resetting back to the chosen expiry length. I may end up with >1 expiry to allow for one that cannot be extended.

When using JWT, session lengths have to be short since JWT isn't really a security method and is subject to token hijacking. So you either have to keep the token lifespan really short or add other session checks such as client IP address.