Node red to OMNI REST API token request

Hi Everyone

New to the community and the node-red platform. Please excuse my unfamiliarity with adding information to posts, etc.

To the point of the post.. I have a Commercial controller that hosts a web server and is REST API capable.
I am having issues with getting the token from the controller. My issue currently is the hashing of the "salt" and including the password.

I'm not sure how to achieve this. i have downloaded pallets that do the function of hashing and un-hashing, if that's what it is, and no success. The API information can be found Here that uses postman (if you're familiar with it).

I have not included any flows currently but will get it together.
Any help or insight would be greatly appreciated.

-Logan

Hello Logan and welcome to the Node-red Forum,

Were you able to make a succesful request to your BMS controller API using the Postman software ?

Hi!

Thanks so much for the reply.

Yes. so the postman works perfectly without a hitch. Just cant get it to work via node red. My eventual objective is to add this to home assistant. but from my constant changing and testing, I'm having such a hard time. its not as simple as copy paste from the official documentation

Currently I can get a reply with the salt.
The rest I cant figure out.

it seems that you need to do a series of http requests
one to get the salt ---> get token ---> request actual data

Try to import this flow (Ctrl-I) , change the function node to the ip/username of your controller
See if you get the salt in NR.

[{"id":"be4cb634d6fc8b19","type":"inject","z":"5847b7aa62131d37","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":350,"y":600,"wires":[["08068f0eee8663e5"]]},{"id":"08068f0eee8663e5","type":"function","z":"5847b7aa62131d37","name":"prepare req","func":"msg = {\n\n    method: 'post',\n    url: 'https://192.168.30.51/api/salt',\n    headers: {\n        'Content-Type': 'application/json'\n    },\n    payload: JSON.stringify({ \"username\": \"Innotech\" })\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":600,"wires":[["de23c9b11c6a7aa8","578d3d4950128bc2"]]},{"id":"de23c9b11c6a7aa8","type":"http request","z":"5847b7aa62131d37","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":740,"y":600,"wires":[["8322c617ff387fcc"]]},{"id":"578d3d4950128bc2","type":"debug","z":"5847b7aa62131d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":540,"wires":[]},{"id":"8322c617ff387fcc","type":"debug","z":"5847b7aa62131d37","name":"salt","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":600,"wires":[]}]

Thanks again.

So i pasted it in and got the following response:

uJXCKFT2M6JhVvi5tIAGwfSWZ4AXvK80a2t8Ararqps=

So it does work.

so where it gets complicated is the part where you need to get the encryption right.
so in postman, it is the following code:

import sha256 from 'crypto-js/sha256'
import base64url from 'base64url'
import Base64 from 'crypto-js/enc-base64'
import Latin1 from 'crypto-js/enc-latin1'

export const hashCredentials = (credential, salt) => {
 const concats = credential + atob(salt)
 const notbase64 = sha256(Latin1.parse(concats))
 const hashedCredential = Base64.stringify(notbase64)
 return base64url.fromBase64(hashedCredential)
}

crypto-js is not in the node red library. this is where i hit a brick wall. Would you have any ideas?

-Logan

Edit:
I have tried numerous pallets. the pallets either don't work or there are issues with errors.

yea i was reading about it now.

Function nodes (in latest Node-red versions) have something called functionExternalModules that give you the ability to require external npm libraries like crypto-js etc
you can read how to set that up in your NR settings.js file here

Once you have that setup you set the modules in the Setup tab of a new Function node (after the salt http request)

And in On Message Tab something like

let salt = msg.payload.client_salt;
let credential = "<your_credential>"

const concats = credential + atob(salt)
const notbase64 = sha256(Latin1.parse(concats))
const hashedCredential = Base64.stringify(notbase64)
let hashCredentials = base64url.fromBase64(hashedCredential)

node.warn({hashCredentials})  // log hashCredentials

Note: One issue i noticed is that Node.js doesnt support the atob() command .. not until version 16

Example Flow using npm atob library :

[{"id":"be4cb634d6fc8b19","type":"inject","z":"5847b7aa62131d37","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":600,"wires":[["08068f0eee8663e5"]]},{"id":"08068f0eee8663e5","type":"function","z":"5847b7aa62131d37","name":"prepare req","func":"msg = {\n\n    method: 'post',\n    url: 'https://192.168.30.51/api/salt',\n    headers: {\n        'Content-Type': 'application/json'\n    },\n    payload: JSON.stringify({ \"username\": \"Innotech\" })\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":600,"wires":[["de23c9b11c6a7aa8","578d3d4950128bc2"]]},{"id":"de23c9b11c6a7aa8","type":"http request","z":"5847b7aa62131d37","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":610,"y":600,"wires":[["8322c617ff387fcc","69fc0d787e6d1653"]]},{"id":"578d3d4950128bc2","type":"debug","z":"5847b7aa62131d37","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":530,"y":540,"wires":[]},{"id":"8322c617ff387fcc","type":"debug","z":"5847b7aa62131d37","name":"salt","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":730,"y":540,"wires":[]},{"id":"69fc0d787e6d1653","type":"function","z":"5847b7aa62131d37","name":"prepare req","func":"let salt = msg.payload.client_salt;\nlet credential = \"<your_cretential>\"\nlet username = \"<your_username>\"\n\nconst concats = credential + atob(salt)\nconst notbase64 = sha256(Latin1.parse(concats))\nconst hashedCredential = Base64.stringify(notbase64)\nlet hashCredentials = base64url.fromBase64(hashedCredential)\n\nnode.warn({hashCredentials})  // log hashCredentials\n\n\nlet data = `{\\r\\n    \"username\": \"${username}\", \\r\\n    \"password\": \"${hashCredentials}\" \\r\\n}`;\n\n msg = {\n  method: 'post',\n  url: 'https://192.168.30.51/api/token',\n  headers: { \n    'Content-Type': 'application/json'\n  },\n  payload : data\n};\n\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"sha256","module":"crypto-js/sha256"},{"var":"base64url","module":"base64url"},{"var":"Base64","module":"crypto-js/enc-base64"},{"var":"Latin1","module":"crypto-js/enc-latin1"},{"var":"atob","module":"atob"}],"x":840,"y":600,"wires":[["12bf0194cfc78c44","a697a6653bd2cfee"]]},{"id":"f616a86568848154","type":"inject","z":"5847b7aa62131d37","name":"Test salt","props":[{"p":"payload.client_salt","v":"uJXCKFT2M6JhVvi5tIAGwfSWZ4AXvK80a2t8Ararqps=","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":680,"y":700,"wires":[["69fc0d787e6d1653"]]},{"id":"12bf0194cfc78c44","type":"debug","z":"5847b7aa62131d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":540,"wires":[]},{"id":"a697a6653bd2cfee","type":"http request","z":"5847b7aa62131d37","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":1050,"y":600,"wires":[["a94527145710a597"]]},{"id":"a94527145710a597","type":"debug","z":"5847b7aa62131d37","name":"token","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1210,"y":600,"wires":[]}]
1 Like

UnborN

You are a legend my friend. works without a hitch.

just for future reference, here is the response:

{"method":"post","url":"https://192.168.50.50/api/token","headers":{"set-cookie":["omni_session=AmVebQZiDGAELlJ2XDwGMVM1VGgCcV8gWjsJd1ckBWlXPFM8VQ0HO1Q2ACsAagQhBG8AZA9nBTkBdVI2VmRaZlRkXGcBOQUyVzUENQczVzICYl4zBjMMPgQwUmBcNwY3U2NUZwJgXzFabQk3V2MFOVcxUzFVYgdjVDcAKwBqBCEEbwBmD2UFOQF1Uj1Wd1oJVDFcNQFlBXJXNgR2B3NXIAI%2FXiQGbQxrBGdSP1wkBjFTPFRgAn1fYlpoCTxXeQU1V2NTfFVgB3BUaQB6AGsEYwRlAG0PdwV2ASRSMVZ1WglUMVw2AWQFblcnBCcHO1dxAj5eYwZmDGAEd1JiXGkGdFMlVHoCO18nWioJdFckBTpXfFN9VTUHO1QmAGEAJAQwBHsANA86BW4BeFInVm5aOFQ0XCMBZAVzVzwEdwdoV3cCd154BjAMNQQhUixcJAY7U3ZUaAJiX2BaZAkmVzsFYVcgUyZVDQczVDEAfQA4BCQEPAAjDywFIQFsUj1WPVpnVGZcYgE0BTRXZAQxBzBXNAIzXmwGJAxgBGxSP1wkBnVTdlQ3AiFfDFo6CWVXIwVhV3FTaVUhB2hUYgAzAHMEcARuACo%3D; path=/; httponly"],"access-control-allow-origin":"*","content-type":"application/json","access-control-allow-methods":"GET, POST, PUT, DELETE, OPTIONS","access-control-allow-headers":"Access-Control-Allow-Headers, Content-Type, Access-Control-Allow-Methods, Authorization, X-Requested-With","strict-transport-security":"max-age=63072000; includeSubDomains; preload","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","content-length":"638","connection":"close","date":"Fri, 29 Oct 2021 02:21:07 GMT","server":"lighttpd","x-node-red-request-node":"57426448"},"payload":{"token":{"token_type":"Bearer","access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ikk1QU9SaUp4ZnFLeDEyZlYrZ2M9In0.eyJpc3MiOjAsInN1YiI6Im1kZk1rdHk1b0dcL0RMYzZvN2xSbHJuNVd5QT09IiwiYXVkIjoiODdjZjg0NTQtYzkyZi00NjI0LTliM2ItM2UzNzdmZTc3NjNhIiwibmJmIjoxNjM1NDc0MDY3LCJpYXQiOjE2MzU0NzQwNjd9.cUMGDJlm9-34jO19w7Q0j3dTNTf2S1ttwKtwfITZVkHOyJrNoPly-GalKEdVAkZRxsJzT6_K0VW5tvyyVtZv6q5v4hD7n_po4s9DA1PCt-OI3VDv9_Jw-neAlRBd5AdhFq5jBXlXCfZze0jv9hE1SXeXE1vIsSOQcslzA6q4SQ1vl5kDv9ItfOQQMCv8UsAZ0btHUVvmzxwxZ7SCu6ON0xG8kuzdxIb0KvqjXChpG6SvVACQ11yA_zTPtyDcKSgqOA933KUstYlySvh21d5qulXyP3z1GEDRqv28fn5Rd7DMX3pMY0bCRz8IFzoQGCbBMPkcGhUKrqZtBHeHZEHp0A"}},"_msgid":"7c35dab03df7da60","statusCode":200,"responseUrl":"https://192.168.50.50/api/token","redirectList":[],"retry":0,"responseCookies":{"omni_session":{"path":"/","value":"AmVebQZiDGAELlJ2XDwGMVM1VGgCcV8gWjsJd1ckBWlXPFM8VQ0HO1Q2ACsAagQhBG8AZA9nBTkBdVI2VmRaZlRkXGcBOQUyVzUENQczVzICYl4zBjMMPgQwUmBcNwY3U2NUZwJgXzFabQk3V2MFOVcxUzFVYgdjVDcAKwBqBCEEbwBmD2UFOQF1Uj1Wd1oJVDFcNQFlBXJXNgR2B3NXIAI/XiQGbQxrBGdSP1wkBjFTPFRgAn1fYlpoCTxXeQU1V2NTfFVgB3BUaQB6AGsEYwRlAG0PdwV2ASRSMVZ1WglUMVw2AWQFblcnBCcHO1dxAj5eYwZmDGAEd1JiXGkGdFMlVHoCO18nWioJdFckBTpXfFN9VTUHO1QmAGEAJAQwBHsANA86BW4BeFInVm5aOFQ0XCMBZAVzVzwEdwdoV3cCd154BjAMNQQhUixcJAY7U3ZUaAJiX2BaZAkmVzsFYVcgUyZVDQczVDEAfQA4BCQEPAAjDywFIQFsUj1WPVpnVGZcYgE0BTRXZAQxBzBXNAIzXmwGJAxgBGxSP1wkBnVTdlQ3AiFfDFo6CWVXIwVhV3FTaVUhB2hUYgAzAHMEcARuACo="}}}

now just need to dabble with it and see what it can do.

Really, thankyou so much.

-Logan

1 Like

Cool .. jeez its like getting authenticated for a nuclear power station :wink:

the rest are more basic requests once you add the Bearer + token in the headers in a similar way in a preceding function.

If it helps you can set on your site the language to Nodejs-Axios , which is a library that most resembles the NR http request properties. except data which should be payload

image

Cool .. jeez its like getting authenticated for a nuclear power station

I know right!
the data is quite secure. only downside is the OMNI will not post. so polling only.. maby something to push up to RND?

i will look into Nodejs-Axios for sure.

does it have something to do with the User group you used to authenticate ?

No. So the access levels simply put restrictions on access.
So, as a BMS controller, Innotech don't want people accessing back end settings and information. They are hidden.

Another example

Facilities manager will have access to setpoints. But staff can only view them.

Access levels are just restrictions.

So by post, I mean if you want to receive an update on an alarm for high temp, you can't. You need to setup something to poll for the information in the event the state changes.

just polling. I understand the problem now.
I guess you'll have to set an Inject node on Repeat interval, to poll the controller as often you see necessary (as far as the device can handle it) in order to get the alarms.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.