Your SICK ID gives you access to our extensive range of services. This includes direct online orders, price and availability checks, and access to our digital services.
Demonstration use The pre-request script in this guide is provided for demonstration and evaluation. It is not hardened for production use. Review, test, and adapt it to your own security requirements before using it in a productive environment.
Overview
1 – What you are building
SICK LiDAR sensors expose a JSON REST API over plain HTTP.
Reading a value (GET) needs no authentication. Writing a value
or running a method (POST) requires
Challenge-Response authentication: the device hands out a
one-time puzzle, and the client answers with a SHA-256 hash chain that
proves it knows the password, without ever sending the password.
Doing that by hand for every request is tedious. This guide sets up
Postman so it happens automatically: you paste a
small script once at the collection level, and every protected
POST is authenticated for you.
What you need
Item
Detail
Postman desktop app
Free. No paid plan required. A free account is needed – without signing in, some features (e.g. Import) are unavailable. Windows / macOS / Linux. Tested with Postman v12.21.2.
Network access to the sensor
Your PC must reach the device IP (default 192.168.0.1).
OpenAPI file for your device
The openapi.yaml that ships with the sensor / firmware. Download it from the SICK product page under Software.
Device credentials
A user level (e.g. Service) and its password.
Firmware Always use the latest available firmware. Check the SICK product page for the latest release.
2 – Install and open Postman
1
Download the free desktop app
Get it from postman.com/downloads and install it like any other application.
2
Sign in with a free account
Create or sign in with a free Postman account. Testing shows that
without an account some features (such as Import) are not available.
A free account is enough – no paid plan or Postman cloud subscription is required.
3
Learn the layout
Left sidebar: Collections (your requests) and
Environments (your variables). Center: the request you are editing.
Bottom: the Console (View → Show Postman Console). You will
use it to confirm the authentication is running.
3 – Import the OpenAPI file to get a collection
Postman can turn the device’s openapi.yaml into a ready-made
collection: a folder of pre-built requests for every endpoint,
so you do not have to type URLs by hand.
Where to get the OpenAPI file
Download it from the sensor’s product page on the SICK website,
in the Software area (alongside firmware and documentation).
Save the openapi.yaml locally before importing.
1
Click Import
At the top of the left sidebar, click the Import icon (next to the + / new-tab button).
2
Choose the OpenAPI file
Drag your openapi.yaml into the window, or click files and select it.
Postman detects the OpenAPI format automatically.
3
Import as a Collection
When prompted, keep the default that generates an API collection
(a “Postman Collection”). Confirm the import.
4
Find your requests
A new collection (e.g. “picoScan150 REST interface description”)
appears under Collections. Expand it to see folders such as
LocationName, EtherIPAddress, … each containing
ready-made GET and POST requests.
Base URL as a variable
OpenAPI imports usually reference the server as {{baseUrl}}.
That is a variable you will fill in next. If your requests instead use a
literal IP, that is fine too. You can still follow the rest of this guide.
4 – Create the environment and enter your password
An environment is a named set of variables (device IP, user, password).
Keeping them here means you never hard-code them into requests and can switch
between devices easily.
1
Create an environment
Left sidebar → Environments → +.
Name it e.g. SICK REST Auth.
2
Add these variables
Variable
Example value
Notes
baseUrl
http://192.168.0.1/api
Scheme + host. Include /api here if your requests are written as {{baseUrl}}/LocationName.
user
Service
The device user level.
password
(enter manually)
Optionally Mark as sensitive. See step 3.
enableAuthentication
true
Master switch. Set false to send requests without auth.
challengePath
/getChallenge
Optional. Where the challenge endpoint lives, relative to baseUrl. With /api already in baseUrl, use /getChallenge.
3
Enter the password manually
In the password row, type your device password into the
Current value column.
You can optionally Mark as sensitive to mask it in the UI.
The Current value stays on your machine only: it is not exported,
not synced, and not shared when you share the collection. Leave
“Initial value” blank so no password is ever written to a shareable file.
4
Activate the environment
Use the environment selector at the top-right of Postman and
choose SICK REST Auth. If it shows “No environment”, your
{{baseUrl}} will not resolve and requests will fail.
Never commit real credentials
Keep the password in the Current value field only. Do not paste it into
the script, a request body, or any file you export or check into version control.
5 – Paste the pre-request script at collection level
A pre-request script runs automatically before a request is sent.
Placed at the collection level, it applies to every request in that
collection, so all protected POSTs get authenticated with one setup.
1
Open the collection editor
Click the collection name in the left sidebar to open it.
2
Go to Scripts → Pre-request
Open the Scripts tab, then the Pre-request sub-tab.
(In current Postman this is the “Scripts” tab; older versions called it
“Pre-request Script”.)
3
Paste the script below and Save
Copy the entire script from §8, paste it in, and click Save.
That is the only code you need. It uses the built-in CryptoJS library,
so there is nothing to install.
New collection = paste again
The script lives inside one collection. If you re-import the OpenAPI file or create a
new collection, paste the script into that collection’s Pre-request tab too.
6 – Send your first authenticated write
Let’s change LocationName. Writing a parameter needs
Challenge-Response only. The script handles it.
1
Open the POST request
In the collection, open the POST request for LocationName
(URL {{baseUrl}}/LocationName).
2
Set a raw JSON body with only data
Body tab → raw → type JSON. Provide just the
data object. The script adds the header automatically
and ignores any placeholder header the OpenAPI template may include.
{
"data": { "LocationName": "my-sensor" }
}
3
Send
Make sure the environment is active and enableAuthentication = true, then click Send.
4
Confirm it worked
Open the Console – click Console in the
status bar at the bottom-left corner of the Postman window (or use
View → Show Postman Console). You should see two calls:
POST http://192.168.0.1/api/getChallenge 200
POST http://192.168.0.1/api/LocationName 200
The device response header should read { "status": 0, "message": "Ok" }.
Prove the auth matters
Set enableAuthentication = false and resend. The device returns
Access Denied. Set it back to true and it succeeds again.
Concept
7 – What the script does under the hood
For any write request (POST/PUT/PATCH/DELETE) the script fetches a
fresh challenge, computes the SHA-256 hash chain, and rewrites the JSON body so
the proof travels in the "header" field alongside your "data".
GET requests are left untouched.
Fed into the hash chain. The password never leaves your machine in clear text over the wire.
enableAuthentication
If not true, the script does nothing.
challengePath
Where getChallenge lives; auto-derived if unset.
Endpoint name in the hash
The script derives the endpoint name from the URL path (stripping a leading
api/ if present), so both {{baseUrl}}/api/LocationName and
{{baseUrl}}/LocationName hash to the same name the device expects.
The Script
8 – Collection-level pre-request script
Copy this entire block into Collection name → Scripts → Pre-request Script.
It is self-contained and uses only Postman’s built-in CryptoJS.
/* =============================================================================
* Challenge-Response Authentication
* Postman collection-level Pre-request Script
* (Collection name -> Scripts -> Pre-request Script)
*
* DEMONSTRATION USE ONLY
* This script is provided for demonstration and evaluation purposes only.
* It is not hardened for production use. Review, test, and adapt it to your
* own security requirements before using it in a productive environment.
*
* Behavior:
* For write requests (POST/PUT/PATCH/DELETE) it fetches a challenge from the
* device, computes the response hash, and embeds an auth `header` object into
* the JSON request body:
* { "header": { nonce, opaque, realm, response, user }, "data": { ... } }
*
* Environment variables:
* baseUrl scheme + host, e.g. http://192.168.0.1 (no trailing /)
* user e.g. Service
* password device password (keep as a secret; never commit)
* enableAuthentication "true" / "false"
* challengePath (optional) challenge endpoint path override
* (auto-derived from the URL style if unset)
* ========================================================================== */
(function () {
'use strict';
function isTrue(value) {
if (value === null || value === undefined || value === '') return false;
try { return JSON.parse(String(value).toLowerCase()) === true; }
catch (e) { return String(value).toLowerCase() === 'true'; }
}
// Convert an array of byte values (0..255) into a CryptoJS WordArray.
function bytesToWordArray(bytes) {
var words = [];
for (var i = 0; i < bytes.length; i++) {
words[i >>> 2] |= (bytes[i] & 0xff) << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, bytes.length);
}
// Pure algorithm: returns the auth `header` object for the request body.
function calculateAuthHeader(user, password, challenge, method, path) {
var ha1;
if (challenge.salt == null) {
ha1 = CryptoJS.SHA256(user + ':' + challenge.realm + ':' + password).toString();
} else {
var prefix = CryptoJS.enc.Latin1.parse(user + ':' + challenge.realm + ':' + password + ':');
ha1 = CryptoJS.SHA256(prefix.concat(bytesToWordArray(challenge.salt))).toString();
}
var ha2 = CryptoJS.SHA256(method + ':' + path).toString();
return {
nonce: challenge.nonce,
opaque: challenge.opaque,
realm: challenge.realm,
response: CryptoJS.SHA256(ha1 + ':' + challenge.nonce + ':' + ha2).toString(),
user: user
};
}
// --- Guard clauses -------------------------------------------------------
var method = pm.request.method ? pm.request.method.toUpperCase() : 'GET';
var fullUrl = pm.variables.replaceIn(pm.request.url.toString());
if (fullUrl.indexOf('/crown') > -1) return;
if (['POST', 'PUT', 'PATCH', 'DELETE'].indexOf(method) === -1) return;
if (fullUrl.indexOf('getChallenge') > -1) return;
if (!isTrue(pm.environment.get('enableAuthentication'))) return;
// --- Required variables --------------------------------------------------
var baseUrl = pm.environment.get('baseUrl');
var user = pm.environment.get('user');
var password = pm.environment.get('password');
if (!baseUrl) throw new Error('[AUTH] Missing environment variable "baseUrl".');
if (!user) throw new Error('[AUTH] Missing environment variable "user".');
if (password === null || password === undefined || password === '') {
throw new Error('[AUTH] Missing environment variable "password".');
}
baseUrl = String(baseUrl).replace(/\/+$/, '');
// --- Derive variable name + challenge URL --------------------------------
// Variable name = URL path after host, with a leading "api/" removed if present,
// so both {{baseUrl}}/api/Name and {{baseUrl}}/Name yield the same name.
var afterScheme = fullUrl.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, '');
var firstSlash = afterScheme.indexOf('/');
var pathPart = (firstSlash === -1 ? '' : afterScheme.substring(firstSlash + 1))
.split('?')[0].split('#')[0].replace(/\/+$/, '');
var hadApiPrefix = false;
if (pathPart.indexOf('api/') === 0) { pathPart = pathPart.substring(4); hadApiPrefix = true; }
var variableName = pathPart.trim();
if (!variableName) throw new Error('[AUTH] Could not determine variable name from URL: ' + fullUrl);
var challengePath = pm.environment.get('challengePath');
if (!challengePath) challengePath = hadApiPrefix ? '/api/getChallenge' : '/getChallenge';
if (challengePath.charAt(0) !== '/') challengePath = '/' + challengePath;
var challengeUrl = baseUrl + challengePath;
// --- Read + resolve the current request body (raw JSON) ------------------
var rawBody = pm.variables.replaceIn((pm.request.body && pm.request.body.raw) ? pm.request.body.raw : '{}');
var bodyContent;
try { bodyContent = JSON.parse(rawBody); }
catch (e) { throw new Error('[AUTH] Request body is not valid JSON: ' + rawBody); }
// --- Fetch challenge, compute header, rewrite body -----------------------
pm.sendRequest({
url: challengeUrl,
method: 'POST',
header: { 'Content-Type': 'application/octet-stream' },
body: { mode: 'raw', raw: JSON.stringify({ data: { user: user } }) }
}, function (err, res) {
if (err) throw new Error('[AUTH] Challenge request failed: ' + err);
if (!res || res.code !== 200) {
throw new Error('[AUTH] Challenge request returned HTTP ' + (res ? res.code : 'no response'));
}
var challenge;
try { challenge = res.json().challenge; }
catch (e) { throw new Error('[AUTH] Challenge response is not valid JSON.'); }
if (!challenge || !challenge.realm || !challenge.nonce) {
throw new Error('[AUTH] Unexpected challenge response format: ' + JSON.stringify(challenge));
}
var header;
try { header = calculateAuthHeader(user, password, challenge, method, variableName); }
catch (e) { throw new Error('[AUTH] Hash calculation failed: ' + e.message); }
pm.request.body.update({
mode: 'raw',
raw: JSON.stringify({ header: header, data: bodyContent.data }),
options: { raw: { language: 'json' } }
});
pm.request.headers.upsert({ key: 'Content-Type', value: 'application/json' });
});
})();
Troubleshooting
9 – Troubleshooting
Symptom
Cause
Fix
getaddrinfo ENOTFOUND {{baseurl}}
No environment selected, so {{baseUrl}} did not resolve.
Select the environment in the top-right selector.
"status": 4, "Access Denied"
The script did not run (no getChallenge in the Console), or wrong credentials.
Confirm the script is in this collection’s Pre-request tab; check user / password.
[AUTH] Missing environment variable "password"
Password empty.
Enter it in the environment’s Current value column.
No auth on a write, no error
enableAuthentication not true, or method is GET.
Set enableAuthentication = true; reads never need auth.
Use the Console as your first stop
View → Show Postman Console. A healthy authenticated write shows a
getChallenge call (200) immediately followed by the endpoint call (200).