User authentication
Goal
Require username/password from clients. The initial accounts are declared with the users: field on the boot config. Each entry has a username, a clear-text password, and a list of role names from the standard OPC UA WellKnownRoles.
users: seeds the accounts on the first boot only. They are written to an archive, role-set.json next to the PKI folder, and from then on that archive is the one source of truth. Accounts and role assignments are managed over OPC UA, with the standard User Management and RoleSet methods (OPC 10000-18), and every change survives a restart. That includes removals: an account you delete stays deleted.
Editing users: afterwards has no effect; the server logs that it was not applied. To start again from users:, stop Node-RED, delete role-set.json, and start it again.
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: "change-me", roles: ["AuthenticatedUser", "SecurityAdmin"], mustChangePassword: true },
{ username: "viewer", password: "viewer", roles: ["AuthenticatedUser", "Observer"] },
],
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:
- Stores each password as a salted scrypt hash; the clear text is never written anywhere. A pre-computed bcrypt hash cannot seed an account and is rejected with an explicit error.
- Maps the role names to the
WellKnownRolesNodeIds for the OPC UA Roles & Permissions framework. - With
mustChangePassword: true, treats the seeded password as temporary: the account holds only theAnonymousrole until the client changes the password with theChangePasswordmethod. Enable it only for clients that support that method.
If you omit users: entirely, the helper seeds a default test set (root/secret, gdsadmin/admingds, user1/password1, user2/password2) suitable for development and OPC UA CTT conformance scenarios. Provide your own users: for production, or remove the test accounts over OPC UA: they do not come back.
Managing accounts over OPC UA
Once the server runs, a client logged in with the SecurityAdmin role, over an encrypted (SignAndEncrypt) connection, manages the accounts with the standard OPC 10000-18 methods:
- User Management (
Server›ServerConfiguration›UserManagement):AddUser,ModifyUser,RemoveUser. Any logged-in user can also change their own password withChangePassword, over an encrypted connection. - RoleSet (
Server›ServerCapabilities›RoleSet› role):AddIdentity/RemoveIdentityto grant or withdraw a role, for a user name, a certificate thumbprint or an application.
Every change is saved to role-set.json at once. Set the STERFIVE_ROLE_SET_SECRET environment variable to encrypt that file at rest. It is a machine setting, never part of the flow.
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.