Interacting with the S1SEVEN API using Node-Red — Part 1
Validating certificates via Node-Red
Update: We have created a simple, custom S1SEVEN node that can significantly simplify this workflow, see how to use and install it here. This article, while more complex to set up, shows more implementation details and can be extended or customized later. For most use cases, we recommend installing and using our custom nodes.
Introduction
Normally interacting with an API requires some development work, but using Node-Red can greatly simplify this process.
Some things that can be done via the API include notarizing a certificate, validating a certificate (checking if it has already been notarized), getting the hash of a certificate, etc. The simplest is certificate validation, as it doesn’t require authentication.
This article will explain each step of the flows required to verify a certificate. Instructions on how to import these pre-made flows will be made available near the end of the article under the heading Importing pre-made flows
.
Getting started
As you can see in the image above, only four steps are needed. Inject the certificate, send an HTTP request, parse the response as JSON and print the result.
An HTTP request is a request that is sent to a server, in this case, S1SEVEN’s servers. Each request receives a response, in this case, the response is set to
msg.payload
.
Uploading a certificate
To simplify the process of injecting a certificate, we have added another flow that allows the user to upload a JSON certificate via the UI, as you can see in the following screenshot:
As you can see in the image above, this flow uses an upload
node which is responsible for adding the upload section to the UI. When the user uploads a file via the UI, msg.payload
is set to the contents of the file in binary format. This is then passed to the json
node, which parses the contents as an object we can interact with.
msg.payload
is the property that is read by default by many nodes.
The Allow access to certificate globally
is a function node containing a small amount of code, global.set(‘certificate’, msg.payload);
that sets the contents of msg.payload
as a property on a globally accessible object, meaning that it can be accessed from any other flow.
Just as
msg
is an object that flows through a particular flow,global
is an object that can be accessed from any flow.
Then the green debug
node prints out the contents of msg.payload
, which in this case would be a JSON certificate in object form.
Verifying the certificate
Now that we have uploaded a certificate and made it accessible to other flows by storing it as a property on the global
object, we can switch back to our verification flow:
In this flow, the blue Verify Certificate
node is used to start the process. If you open it by double-clicking the node, you will see that it sets msg.payload
to a timestamp. This is the default behavior of an inject node.
Notice that we have added a Get certificate
node to this flow. This is to simplify the flow and allows us to retrieve the uploaded certificate. Our Get certificate
node contains a little more code than its corresponding function node in the upload flow, but it’s pretty straightforward to understand.
const certificate = global.get('certificate');
if (certificate) {
msg.payload = certificate;
return msg;
} else {
node.warn('No certificate found, please upload a JSON
certificate via the UI');
}
On the first line, we retrieve the certificate that has been saved at global.certificate
and store it in a variable called certificate
.
A variable is a bit like a box in that any value can be stored in it, or it can be empty.
Then, on line two, we check if certificate
contains a value. If it does, we store that value in msg.payload
. The line return msg;
sends the msg
object to the next node.
If certificate
does not contain a value, we use node.warn
to print a message to the debug sidebar, asking the user to upload a JSON certificate.
The next node in our flow is the http request
node. This node is used to send a request to S1SEVEN’s servers. You can double-click on the node to see the options:
We are primarily interested in the first two settings, Method
and URL
. The Method
we want to set to POST
, as this is a type of request that allows us to send a file along with the request. In this case, we want to send the certificate.
When
Method
is set toPOST
, the contents ofmsg.payload
are sent along with the request.
URL
is pretty self-explanatory; it contains the URL we want to send the request to. In this case, the URL is set to https://app.s1seven.dev/api/certificates/verify?mode=test
.
As we mentioned above, every HTTP request receives a response. msg.payload
is set to the response value, which is then passed into the json
node to be parsed into an object we can interact with.
That object is then passed to the green debug node, where its contents are printed in the debug panel to the right. Here is a sample of what that object can contain:
When validating a certificate, we are primarily interested in the first property, isValid
. isValid
will either be true
if the certificate is valid and has been notarized or false
if the certificate has not been notarized.
The hash
object contains extra data about the certificate hash, including its hash value, the algorithm used, and the encoding.
So simply put, all we have to do is check whether isValid
is true
or false
, and we can verify whether a certificate has been notarized or not!
Importing pre-made flows
To simplify the process, instead of replicating our flows, our pre-made flows can be easily imported into Node-Red and modified if necessary.
Click on the hamburger menu (the three horizontal lines) at the top right of the Node-Red toolbar (beside the Deploy button) and click import:
Then choose Clipboard
, and copy-paste the code from further down in this page into the box, then click the redImport
button:
The pre-made flows will automatically be imported, and you can begin using them immediately!
Here is the flow for allowing JSON certificates to be uploaded via the UI:
[
{
"id": "3064fedc7d32c913",
"type": "tab",
"label": "JSON certificate input via UI",
"disabled": false,
"info": "",
"env": []
},
{
"id": "b32669d638cb2d0e",
"type": "debug",
"z": "3064fedc7d32c913",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 100,
"wires": []
},
{
"id": "4498c50d358dd7ee",
"type": "function",
"z": "3064fedc7d32c913",
"name": "Allow access to certificate globally",
"func": "global.set('certificate', msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 540,
"y": 100,
"wires": [
[
"b32669d638cb2d0e"
]
]
},
{
"id": "02e44634367bb213",
"type": "json",
"z": "3064fedc7d32c913",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 310,
"y": 100,
"wires": [
[
"4498c50d358dd7ee"
]
]
},
{
"id": "5873882101bad2f8",
"type": "ui_upload",
"z": "3064fedc7d32c913",
"group": "06fcee6ae387d692",
"title": "upload",
"name": "",
"order": 3,
"width": 0,
"height": 5,
"chunk": 256,
"transfer": "binary",
"x": 170,
"y": 100,
"wires": [
[
"02e44634367bb213"
]
]
},
{
"id": "06fcee6ae387d692",
"type": "ui_group",
"name": "API input",
"tab": "998ca7fbf9a4874e",
"order": 1,
"disp": true,
"width": "10",
"collapse": false,
"className": ""
},
{
"id": "998ca7fbf9a4874e",
"type": "ui_tab",
"name": "API input",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
Here is the flow for validating a certificate via an HTTP request:
[
{
"id": "26f6e289b3fab452",
"type": "tab",
"label": "Validate Certificate",
"disabled": false,
"info": "",
"env": []
},
{
"id": "8e060cd8e0287093",
"type": "inject",
"z": "26f6e289b3fab452",
"name": "Verify Certificate",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 160,
"wires": [
[
"eb3589bc5a1eb08c"
]
]
},
{
"id": "05e799dddf96b541",
"type": "http request",
"z": "26f6e289b3fab452",
"name": "",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "https://app.s1seven.ovh/api/certificates/verify?mode=test",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"x": 610,
"y": 160,
"wires": [
[
"ecc3ac7d13f75bf0"
]
]
},
{
"id": "314196406ec27dd6",
"type": "debug",
"z": "26f6e289b3fab452",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 910,
"y": 160,
"wires": []
},
{
"id": "ecc3ac7d13f75bf0",
"type": "json",
"z": "26f6e289b3fab452",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 770,
"y": 160,
"wires": [
[
"314196406ec27dd6"
]
]
},
{
"id": "eb3589bc5a1eb08c",
"type": "function",
"z": "26f6e289b3fab452",
"name": "Get certificate",
"func": "const certificate = global.get('certificate');\nif (certificate) {\n msg.payload = certificate;\n return msg;\n} else {\n node.warn('No certificate found, please upload a JSON certificate via the UI');\n}\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 420,
"y": 160,
"wires": [
[
"05e799dddf96b541"
]
]
}
]
Conclusion
Thank you for reading this article. For more information, please get in touch with us at https://s1seven.com or support@s1seven.com.
For information on how to notarize certificates via Node-Red, click here to read Part 2.