Authorization in Node-Red

Hi all, I'm trying to build some automations for reporting through Node-Red using the SKY API. I'm having a difficult time understanding how to authenticate through this and am wondering if anyone has any experience with this they might be able to share, particularly how to generate an authentication token and begin making API calls. I am able to do this in Postman via one-off testing, but can't figure it out in Node-Red.

Comments

  • Hi Rainer,

    I created some flows this weekend to show how to authorize and make an API call with Node-Red.

    You will need several values:
    1. Your SKY API Subscription ID (Primary access key)
    2. The client ID of your SKY Application
    3. The client secret of your SKY Application
    4. You will need to create a valid refresh token with Postman or another API tool.

    I created a JSON file locally to store the configuration values needed to authorize and make API requests. I used the path 'd:\\oauth\\token.txt' but you can store the file where you want and change the paths where the file is READ and WRITTEN to.

    5ee6ae3bd18cb853c28656f6c66d1dee-huge-im
    fc24af866f6026f73206283b71c040ed-huge-im

    The configuration file should look like this, except you will need to replace the {tokens} with the correct values.

    {
    "grant_type": "refresh_token",
    "refresh_token": "{valid-refresh-token}",
    "subscription_id": "{your-subscription-id}",
    "client_id": "{your-client-id}",
    "client_secret": "{your-client-secret}"
    }


    The flow will use a subflow to read the token file and then use those values to attempt a Constituent List API call. If the call fails with a HTTP 401 Unauthorized, it will use a subflow to refresh token data and attempt the call again.

    3abb53a782135d70d8129bac09cabc72-huge-im

    The refresh token subflow is where you will find how to make a call to get an access token and a new refresh token.

    66bb89ff641d15d024adf18ae3b5251f-huge-im

    I hope this helps! Good luck on your project!

  • Here is the export of the flows

    [{"id":"181d7cfca17e0bfb","type":"subflow","name":"Refresh Token","info":"","category":"","in":[{"x":40,"y":140,"wires":[{"id":"37f2b11ae33a743c"}]}],"out":[{"x":1000,"y":180,"wires":[{"id":"de3e18c4ede4d340","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"b11e447f5f2a21be","type":"http request","z":"181d7cfca17e0bfb","name":"Token Request","method":"POST","ret":"obj","paytoqs":"body","url":"https://oauth2.sky.blackbaud.com/token","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":460,"y":180,"wires":[["a65f17a72e0094af"]]},{"id":"0a24218760d9b123","type":"function","z":"181d7cfca17e0bfb","name":"Token Request Input","func":"var formBody = [];\\n\\nfor (var property in msg.payload) {\\n var encodedKey = encodeURIComponent(property);\\n var encodedValue = encodeURIComponent(msg.payload[property]);\\n formBody.push(encodedKey + \\"=\\" + encodedValue);\\n}\\nmsg.payload = formBody.join(\\"&\\");\\nmsg.headers = {};\\nmsg.headers[\\"contnet-type\\"] = \\"application/x-www-form-urlencoded\\";\\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":120,"wires":[["b11e447f5f2a21be"]]},{"id":"de3e18c4ede4d340","type":"file","z":"181d7cfca17e0bfb","name":"Write Token File","filename":"D:\\\\oauth\\\\token.txt","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"none","x":740,"y":180,"wires":[["a62c1e8637c819f0"]]},{"id":"a62c1e8637c819f0","type":"debug","z":"181d7cfca17e0bfb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1050,"y":240,"wires":[]},{"id":"a65f17a72e0094af","type":"function","z":"181d7cfca17e0bfb","name":"Get Global Token Request","func":"var token_request = global.get(\\"token_request\\");\\ntoken_request.refresh_token = msg.payload.refresh_token;\\ntoken_request.access_token = msg.payload.access_token;\\nmsg.payload = token_request;\\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":120,"wires":[["de3e18c4ede4d340"]]},{"id":"37f2b11ae33a743c","type":"subflow:434e67626ed4b9e7","z":"181d7cfca17e0bfb","name":"","x":200,"y":140,"wires":[["0a24218760d9b123"]]},{"id":"434e67626ed4b9e7","type":"subflow","name":"Read Token File","info":"","category":"","in":[{"x":80,"y":140,"wires":[{"id":"09a15a022f4f22c9"}]}],"out":[{"x":500,"y":340,"wires":[{"id":"d7743ece37313e41","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"09a15a022f4f22c9","type":"file in","z":"434e67626ed4b9e7","name":"Read Token Request File","filename":"d:\\\\oauth\\\\token.txt","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":290,"y":140,"wires":[["5389282abf0bc48a"]]},{"id":"5389282abf0bc48a","type":"json","z":"434e67626ed4b9e7","name":"Request","property":"payload","action":"obj","pretty":false,"x":300,"y":240,"wires":[["d7743ece37313e41"]]},{"id":"d7743ece37313e41","type":"function","z":"434e67626ed4b9e7","name":"Set Token Request Variable","func":"global.set(\\"token_request\\", msg.payload);\\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":340,"wires":[[]]},{"id":"bb809836.cac238","type":"tab","label":"Constituent List","disabled":false,"info":""},{"id":"a5eb5876edef9083","type":"function","z":"bb809836.cac238","name":"Constituent List Headers","func":"msg.headers = {};\\nmsg.headers[\\"Bb-Api-Subscription-Key\\"] = msg.payload.subscription_id;\\nmsg.headers[\\"Authorization\\"] = \\"Bearer \\" + msg.payload.access_token;\\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":280,"wires":[["b522d02cd6adde3d"]]},{"id":"b522d02cd6adde3d","type":"http request","z":"bb809836.cac238","name":"Constituent List Request","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://api.sky.blackbaud.com/constituent/v1/constituents","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":730,"y":280,"wires":[["13c7c914deb17892","abafc78a5d2e2012"]]},{"id":"e2a4b1560e3a1e1e","type":"inject","z":"bb809836.cac238","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":280,"wires":[["4db6724b19edfb67"]]},{"id":"4db6724b19edfb67","type":"subflow:434e67626ed4b9e7","z":"bb809836.cac238","name":"","x":280,"y":280,"wires":[["a5eb5876edef9083"]]},{"id":"13c7c914deb17892","type":"debug","z":"bb809836.cac238","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":950,"y":280,"wires":[]},{"id":"abafc78a5d2e2012","type":"switch","z":"bb809836.cac238","name":"Should Refresh","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"401","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":720,"y":180,"wires":[["63f2bcb8530a0c1f"]]},{"id":"63f2bcb8530a0c1f","type":"subflow:181d7cfca17e0bfb","z":"bb809836.cac238","name":"","x":720,"y":80,"wires":[["4db6724b19edfb67"]]}]

Categories