Skip to main content

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.

Seeded once, then managed over OPC UA

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 WellKnownRoles NodeIds for the OPC UA Roles & Permissions framework.
  • With mustChangePassword: true, treats the seeded password as temporary: the account holds only the Anonymous role until the client changes the password with the ChangePassword method. 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 with ChangePassword, over an encrypted connection.
  • RoleSet (Server › ServerCapabilities › RoleSet › role): AddIdentity / RemoveIdentity to 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:

  • Anonymous
  • AuthenticatedUser
  • Observer
  • Operator
  • Engineer
  • Supervisor
  • ConfigureAdmin
  • SecurityAdmin

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 (preferably SignAndEncrypt); 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.