node-opcua-role-set-server
v2.175.2
Published
pure nodejs OPCUA SDK - server-side role-set management (OPC 10000-18)
Downloads
609
Maintainers
Readme
node-opcua-role-set-server
Server-side RoleSet management for OPC UA (OPC 10000-18 — Role-Based Security).
This package wires the RoleSet object of an OPC UA server to an identity-mapping
store. It binds the AddIdentity / RemoveIdentity methods on each Role, keeps each
Role's Identities property in sync with the store, registers a role resolver that
maps incoming user identity tokens to roles, and optionally persists the mappings to
disk.
It builds on node-opcua-role-set-common for the
store, persistence and well-known role NodeIds.
see http://node-opcua.github.io/
Installation
$ npm install node-opcua-role-set-serverQuick start
Call installRoleSet after the server has started, when the address space is
available:
import { OPCUAServer } from "node-opcua";
import { installRoleSet } from "node-opcua-role-set-server";
const server = new OPCUAServer({ /* ... */ });
await server.start();
const { store, restrictionStore, resolver } = await installRoleSet(server, {
persistencePath: "./config/role-set.json",
persistenceSecret: process.env.ROLE_SET_SECRET // optional: encrypt at rest
});What installRoleSet does:
- Finds the
RoleSetnode (i=15606) in the address space. - Loads the whole configuration from the consolidated archive at
persistencePath(if any): identity mappings, custom Role definitions and application/endpoint restrictions. - Registers a
RoleSetResolveronserver.roleResolversso sessions are mapped to roles at login. - For each Role in the RoleSet:
- sets the initial
Identities(and restriction) property values from the stores; - binds the
AddIdentity/RemoveIdentityand the application/endpoint restriction methods.
- sets the initial
- Binds
RoleSet.AddRole/RoleSet.RemoveRole(§4.2): custom Roles are created asns=1;g=<uuid>instances ofRoleType(collision-proof, stable across restarts); well-known Roles cannot be removed. - After every mutation, refreshes the Role variables and atomically rewrites the archive.
Returned values
| Field | Type | Description |
|-------|------|-------------|
| store | IIdentityMappingStore | The identity-mapping store backing the RoleSet. |
| restrictionStore | IRoleRestrictionStore | The per-Role application/endpoint restriction store. |
| resolver | RoleSetResolver | The resolver registered on server.roleResolvers. |
You can mutate store directly (e.g. to seed default mappings); call sites that go
through the bound methods automatically persist, but direct store changes are not
written to the archive until the next method-driven mutation.
User Management & one-call setup
This package also installs User Management (§5) and offers a one-call helper that wires roles and users to a single source of truth. The complete end-to-end walkthrough lives in the Role-Based Security & User Management guide; in short:
import { createRoleBasedSecurity } from "node-opcua-role-set-server";
import { WellKnownRoleIds } from "node-opcua-role-set-common";
const security = await createRoleBasedSecurity({
users: [{ userName: "admin", password: "admin-pw1", roles: [WellKnownRoleIds.SecurityAdmin] }]
});
const server = new OPCUAServer({ /* … */ userManager: security.userManager });
await server.start();
await security.install(server, { persistencePath: "./config/role-set.json" });createRoleBasedSecurity is async (it hashes any clear-text seed passwords)
and returns the shared stores plus the userManager bridge.
Seeding without clear text, and password hashing
Each seeded user provides one of:
password— clear text (hashed with scrypt on seed); orpasswordHash— a pre-hashed credential record, so no clear text lives in config / version control.
Build a record offline with the node-opcua-role-set-common helpers:
import { serializeUser, credentialRecord } from "node-opcua-role-set-common";
// scrypt record from a clear password (run once at deploy time, commit the result)
const admin = await serializeUser("admin", "admin-pw1", { /* userConfiguration, description */ });
// or wrap an already-hashed credential you already hold (e.g. a legacy $2b$ bcrypt hash)
const legacy = credentialRecord("legacy", "$2b$10$……");
const security = await createRoleBasedSecurity({
users: [
{ userName: "admin", passwordHash: admin, roles: [WellKnownRoleIds.SecurityAdmin] },
{ userName: "legacy", passwordHash: legacy, roles: [WellKnownRoleIds.Operator] }
]
});Credentials are stored as self-describing PHC strings ($scrypt$… / $2b$…), so
schemes coexist: new/changed passwords hash with scrypt (Node core), while a
legacy bcrypt credential still verifies and is transparently re-hashed to
scrypt on the next successful login (upgrade-on-login). Pass a custom
registry to createRoleBasedSecurity to add schemes (e.g. an external
verifier). See node-opcua-role-set-common.
Exports
| Export | Description |
|--------|-------------|
| createRoleBasedSecurity(options?) | Recommended — one user store + one identity store behind the userManager bridge, with a two-phase install(server, …). |
| installRoleSet(server, options?) | Install RoleSet management (Roles, identities, restrictions, AddRole/RemoveRole). |
| installUserManagement(server, options?) | Install User Management (§5): AddUser / ModifyUser / RemoveUser / ChangePassword. |
| createUserManager(userStore, identityStore) | Build the server userManager bridge from a shared user + identity store. |
| InstallRoleSetOptions | { store?, persistence?, persistencePath?, persistenceSecret? }. |
| InstallRoleSetResult | { store, restrictionStore, resolver }. |
| InstallUserManagementOptions / InstallUserManagementResult | User Management install options / result. |
| IServerForRoleSet / IServerForUserManagement | Minimal server shapes required. |
| RoleSetResolver | Adapts an IIdentityMappingStore to the server's IRoleResolver contract. |
| make…Handler factories | Build the Method handlers (used internally; exposed for custom binding). |
| checkSecurityAdminAccess / checkEncryptedChannel | Authorization helpers (SecurityAdmin Role / encrypted channel). |
| raiseAuditMethodEvent | Raise an AuditUpdateMethodEventType (no secrets). |
Security model
The sensitive RoleSet & User Management nodes are protected with the address space's own enforcement, so the checks run in the core before any bound handler (the per-Method guards are defense-in-depth):
- Administrative Methods require
SecurityAdminover an encrypted channel — AddRole/RemoveRole, AddIdentity/RemoveIdentity, the restriction Methods and AddUser/ModifyUser/RemoveUser.ChangePasswordstays callable by any authenticated user (still encrypted). - Non-admins cannot Browse them (§4.4.1): each Role's
Identities/Applications/EndpointsProperties and config Methods, plus theUserslist, carry SecurityAdmin-only RolePermissions, so they are hidden and unreadable. Role nodes themselves stay browsable. EncryptionRequiredon those nodes → reads over a None/Sign channel returnBad_SecurityModeInsufficient.- Disabling (ModifyUser) or removing (RemoveUser) a user terminates their live sessions (§5.2.6-7), not just their ability to re-authenticate.
Status codes returned by the identity Method handlers:
| Status | Condition |
|--------|-----------|
| Good | The identity was added / removed. |
| BadUserAccessDenied | The session does not hold the SecurityAdmin role. |
| BadSecurityModeInsufficient | The channel is not SignAndEncrypt. |
| BadInvalidArgument | The input argument is not an IdentityMappingRuleType. |
| BadNoMatch | RemoveIdentity — no matching rule was found. |
| BadAlreadyExists | AddRole — the RoleName duplicates an existing (well-known or custom) Role. |
| BadRequestNotAllowed | RemoveRole — well-known Roles cannot be removed. |
Persistence
When persistencePath is supplied, the entire RoleSet configuration is kept in a
single consolidated archive (identity mappings, custom Role definitions and
application/endpoint restrictions):
- the archive is loaded on install (a missing file is a no-op);
- it is atomically rewritten (temp file + rename) after every successful mutation, so a crash can never leave a half-written file;
- when
persistenceSecretis set the whole payload is encrypted at rest with AES-256-GCM (key derived from the secret via scrypt); otherwise it is plain, human-readable JSON with the identity mappings embedded as a base64 binary blob.
The archive format (readArchive / writeArchive, version-checked) is defined in
node-opcua-role-set-common.
Sharing the archive with User Management
To keep users (salted scrypt hashes, never clear passwords) in the same file as the
role configuration, create one ArchiveStore and pass it to both installers:
import { ArchiveStore } from "node-opcua-role-set-common";
const persistence = new ArchiveStore("./config/role-set.json", {
secret: process.env.ROLE_SET_SECRET // optional encryption
});
await installRoleSet(server, { persistence });
await installUserManagement(server, { persistence });The coordinator gathers every registered section (identities / roles / restrictions / users)
and rewrites the one file atomically on each mutation, so neither installer clobbers the
other — a section with no provider yet keeps its last loaded value, so the result is
independent of install order. Pass only persistencePath instead for a role-config-only
(or users-only) file.
Related packages
node-opcua-role-set-common— shared types, identity/user stores and persistence.node-opcua-role-set-client— client-side role browsing, identity & user management.node-opcua-role-set-admin— therole-set-admincommand-line tool.- Role-Based Security & User Management guide — cross-package overview & getting started.
License
Node-OPCUA is made available to you under the MIT open source license.
See LICENSE for details.
Copyright
Copyright (c) 2022-2026 Sterfive SAS - https://www.sterfive.com
Copyright (c) 2014-2022 Etienne Rossignon
