User authentication
Goal
Require username/password from clients. Authentication is configured declaratively with the users: field on the boot config. Each entry has a username, a password (clear text or a pre-computed bcrypt hash), and a list of role names from the standard OPC UA WellKnownRoles.
:::info No callback escape hatch
A custom user-manager callback would have to live in a Function-node body — and that body re-runs on every message, so the callback identity would churn and racy state could leak. The palette therefore exposes only the declarative users: field. If your authentication source is dynamic (database, LDAP, single-sign-on), expose it through your own back-end and synchronise the resulting user list into users: at deploy time, not per request.
:::
The users: array is part of the config-identity hash, so changing it in the boot Function and redeploying triggers an automatic teardown-then-rebuild. Do not put user-list edits in a node that runs per message.
Inline users
const sterfive = global.get("sterfive");
if (!sterfive) {
node.error("global.get('sterfive') is not set — is the Sterfive OPC UA palette loaded?");
} else {
const { bootstrap, opcua } = sterfive;
const { bootstrapServer } = bootstrap;
const cfg = msg.config || {};
const handle = await bootstrapServer({
port: cfg.port ?? 4840,
endpoint: cfg.endpoint || "node-red-server",
nodesets: ["standard"],
users: [
{ username: "admin", password: "secret", roles: ["AuthenticatedUser", "ConfigureAdmin"] },
{ username: "viewer", password: "$2b$10$...", roles: ["AuthenticatedUser"] },
],
onPopulate: (addressSpace, exposed) => {
const ns = addressSpace.getOwnNamespace();
exposed.myVariable = ns.addVariable({
organizedBy: "RootFolder",
nodeId: "s=MyDynamicVariable",
browseName: "MyDynamicVariable",
dataType: "Double",
value: { dataType: opcua.DataType.Double, value: 0.0 },
});
},
});
flow.set("$myVariable", handle.exposed.myVariable);
flow.set("$opcuaHandle", handle);
node.send({ payload: `OPC UA Server running at ${handle.server.getEndpointUrl()}` });
}
Behind the scenes the helper:
- Hashes any clear-text password with bcrypt at boot time.
- Compares incoming
ActivateSessioncredentials withbcrypt.compare. - Recognises pre-computed bcrypt hashes (
$2a$/$2b$/$2y$prefix) and uses them verbatim. - Maps the role names to the
WellKnownRolesNodeIds for the OPC UA Roles & Permissions framework.
If you omit users: entirely, the helper installs a default test set (root/secret, gdsadmin/admingds, user1/password1, user2/password2) suitable for development and OPC UA CTT conformance scenarios. Override users: for production.
Helpers exposed for tooling
Two helpers are available on bootstrap for hashing passwords ahead of time:
const { bootstrap } = global.get("sterfive");
const { ensureBcryptHash, isBcryptHash } = bootstrap;
isBcryptHash("$2b$10$..."); // true
ensureBcryptHash("plain"); // returns a bcrypt hash; passes through if already hashed
Use these in a deploy script if you want to commit hashed passwords to a Git-tracked config rather than checking in clear text.
Available role names
The roles: field accepts any standard OPC UA WellKnownRoles name, including:
AnonymousAuthenticatedUserObserverOperatorEngineerSupervisorConfigureAdminSecurityAdmin
Per-node read/write authorization comes from access-level attributes on the variables themselves combined with the role mapping above. The user list controls only session activation and the role NodeIds attached to the session.
Combining with anonymous access
allowAnonymous: false disables anonymous sessions entirely. The default is true. To allow anonymous reads while requiring credentials for writes, leave anonymous on and gate writes via per-variable access-level attributes.
Notes
- For credentials over the wire, configure at least
MessageSecurityMode.Sign(preferablySignAndEncrypt); otherwise the username/password travels in cleartext. See Security policies and certificates.
Next step
Continue with security policies and certificates.
Further reading
For more tips and examples, see the Sterfive book node-opcua by example.