Postman¶
Postman can import the sensor OpenAPI file and automatically authenticate protected REST writes with a collection-level pre-request script. This setup is intended for demonstration and evaluation; review, test, and adapt it before production use.
What you need¶
Install the free Postman desktop application and sign in with a free account. Your computer must reach the sensor, normally at 192.168.0.1. Download the openapi.yaml matching the device firmware from the SICK product page under Software, and have the password for an enabled user level available. See User levels.
Import the OpenAPI file¶
In Postman, select Import, choose the downloaded openapi.yaml, and import it as a collection. The resulting collection contains prepared requests for the sensor endpoints. Imports normally use {{baseUrl}}; this variable is configured in the next step.
Create an environment¶
Create an environment named, for example, SICK REST Auth, add the following variables, and select it in Postman's environment selector.
| Variable | Example value | Purpose |
|---|---|---|
baseUrl |
http://192.168.0.1/api |
Sensor REST API base URL |
user |
Service |
Enabled sensor user level |
password |
Enter in Current value only | Device password |
enableAuthentication |
true |
Enables the script |
challengePath |
/getChallenge |
Challenge endpoint, relative to baseUrl |
Keep the password's Initial value field empty so it is not exported or shared. Mark it as sensitive if available. Do not put credentials in requests, scripts, exported collections, or version control.
Add the pre-request script¶
Open the imported collection, choose Scripts > Pre-request, paste the following script, and save. A collection-level script applies to every request in that collection; repeat this step after importing a new collection.
(function () {
"use strict";
function isTrue(value) {
return String(value).toLowerCase() === "true";
}
function bytesToWordArray(bytes) {
const words = [];
for (let index = 0; index < bytes.length; index += 1) {
words[index >>> 2] |= (bytes[index] & 0xff) << (24 - (index % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, bytes.length);
}
function calculateAuthHeader(user, password, challenge, method, endpoint) {
const hash1 = challenge.salt == null
? CryptoJS.SHA256(`${user}:${challenge.realm}:${password}`).toString()
: CryptoJS.SHA256(
CryptoJS.enc.Latin1.parse(`${user}:${challenge.realm}:${password}:`)
.concat(bytesToWordArray(challenge.salt)),
).toString();
const hash2 = CryptoJS.SHA256(`${method}:${endpoint}`).toString();
return {
nonce: challenge.nonce,
opaque: challenge.opaque,
realm: challenge.realm,
response: CryptoJS.SHA256(`${hash1}:${challenge.nonce}:${hash2}`).toString(),
user,
};
}
const method = pm.request.method.toUpperCase();
const url = pm.variables.replaceIn(pm.request.url.toString());
if (
!["POST", "PUT", "PATCH", "DELETE"].includes(method) ||
url.includes("/getChallenge") ||
!isTrue(pm.environment.get("enableAuthentication"))
) {
return;
}
const baseUrl = pm.environment.get("baseUrl")?.replace(/\/+$/, "");
const user = pm.environment.get("user");
const password = pm.environment.get("password");
if (!baseUrl || !user || !password) {
throw new Error("[AUTH] Set baseUrl, user, and password in the active environment.");
}
const path = new URL(url).pathname.replace(/^\/api\//, "").replace(/^\/+|\/+$/g, "");
if (!path) {
throw new Error(`[AUTH] Cannot determine endpoint from ${url}.`);
}
const rawBody = pm.variables.replaceIn(pm.request.body?.raw || "{}");
let body;
try {
body = JSON.parse(rawBody);
} catch {
throw new Error("[AUTH] The request body must be valid JSON.");
}
const challengePath = pm.environment.get("challengePath") || "/getChallenge";
pm.sendRequest({
url: `${baseUrl}${challengePath.startsWith("/") ? "" : "/"}${challengePath}`,
method: "POST",
header: { "Content-Type": "application/json" },
body: { mode: "raw", raw: JSON.stringify({ data: { user } }) },
}, (error, response) => {
if (error) throw new Error(`[AUTH] Challenge request failed: ${error}`);
if (!response || response.code !== 200) {
throw new Error(`[AUTH] Challenge returned HTTP ${response?.code ?? "no response"}.`);
}
const challenge = response.json().challenge;
if (!challenge?.realm || !challenge?.nonce) {
throw new Error("[AUTH] Unexpected challenge response.");
}
pm.request.body.update({
mode: "raw",
raw: JSON.stringify({
header: calculateAuthHeader(user, password, challenge, method, path),
data: body.data,
}),
options: { raw: { language: "json" } },
});
pm.request.headers.upsert({ key: "Content-Type", value: "application/json" });
});
}());
Send an authenticated write¶
Open the POST request for LocationName, set the body type to raw JSON, and provide only the data object:
With the environment active and enableAuthentication set to true, send the request. The Postman Console should show a successful POST to getChallenge, followed by the request to LocationName. A response header with "status": 0 confirms success. Set enableAuthentication to false to verify that the device rejects the protected write.
The script leaves GET requests unchanged. For each write request, it obtains a fresh one-time challenge, calculates the SHA-256 challenge-response hash, and adds the resulting authentication header to the outgoing JSON body. See Challenge Response Authentication for the underlying protocol and REST API / OpenAPI for endpoint documentation.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
getaddrinfo ENOTFOUND {{baseUrl}} |
No active environment | Select the environment in the top-right selector. |
Access Denied |
Script did not run, user level is disabled, or credentials are wrong | Confirm the collection-level script and enabled user level; check credentials. |
| Missing-variable error | Environment values are incomplete | Set baseUrl, user, and the Current value for password. |
| No authentication on a write | Authentication is disabled or request is a GET |
Set enableAuthentication to true; GET requests do not need authentication. |
The Postman Console is the first diagnostic step: a healthy protected request shows a getChallenge call immediately before the endpoint call.