OPC UA Role-Based Security in Practice
Implementing OPC 10000-18 with NodeOPCUA 2.174.0
TL;DR
- NodeOPCUA 2.174.0 ships a complete, spec-conformant implementation of OPC 10000-18 (Role-Based Security and User Management), in open source under MIT.
- Servers can now model who a connecting user is, which Roles they hold, and what each Role is allowed to do, and administer all of it at runtime over OPC UA itself.
- Out of the box you get the standard
RoleSetwith the well-known Roles (Anonymous, AuthenticatedUser, Operator, Observer, SecurityAdmin, ConfigureAdmin), plusAddRole/RemoveRolefor custom Roles.- Identity mapping supports UserName, Thumbprint, full X.509 Distinguished-Name criteria, plus JWT
Role/GroupIdclaims andApplication/TrustedApplicationmappings, all administered live viaAddIdentity/RemoveIdentity.- One-call setup with
createRoleBasedSecurity(). Optional AES-256-GCM-encrypted persistence that survives restarts. Salted scrypt password hashing. Full audit events that never leak credentials.
Why this release matters
Most OPC UA breaches observed in the field do not break the cryptography. They walk through security that was specified at design time, then quietly turned off at deployment. Bitsight's 2025 scan found 14,220 internet-exposed OPC UA servers across 99 countries, of which 51.74% allowed unauthenticated access [1]. A 2021 CISPA study of 48 OPC UA products found that 38 carried at least one security issue and 7 did not support security features at all [2]. Almost always, the problem sat in implementation and configuration, not in the protocol itself.
The protocol gives operators every tool they need. What has historically been missing is the operational layer that makes those tools usable at the scale of a real industrial site. We call this the deployment gap, and have been documenting it on this blog and in the Sterfive white paper [3].
NodeOPCUA 2.174.0 closes the authorisation half of that gap in the open-source stack itself. Combined with OPC UA's certificate-based authentication (which NodeOPCUA has shipped since day one) and the upcoming Sterfive GDS Server for centralised certificate lifecycle management (issuing, renewing, and revoking the digital certificates every device uses to prove its identity), the operational security layer that previously had to be hand-rolled is now available out of the box.
What OPC 10000-18 (Part 18) defines
For a complete, vendor-neutral walkthrough of Part 18 itself, see our reference TL;DR: OPC UA Part 18, Role-Based Security [4]. Read it first if you are new to the spec.
In short, Part 18 is the information model by which an OPC UA Server decides who is allowed to do what in its AddressSpace (the catalogue of variables, methods, and objects the Server exposes to clients). It does not invent new permissions (those live in Part 3 [5]). It defines the plumbing that connects identities (who is connecting) to Roles (named buckets of privileges), plus a standard set of operations to manage local users.
Think of it as the Role-Based Access Control (RBAC) engine of OPC UA: the same idea as Unix groups, Windows security groups, or cloud Identity and Access Management (IAM) roles, applied to industrial communication.
The two-part conceptual model:
| Question | Term | Where it's answered |
|---|---|---|
| "Who are you?" | Authentication | At session creation (Part 4 [6]) via user identity tokens and application certificates |
| "What may you do?" | Authorization | Per-Node, via RolePermissions granted to your Roles (Part 3 [5]) |
A Session can hold many Roles at once. The Server evaluates every Role's mapping rules against the Session's properties (user token, client certificate, endpoint URL) at activation time and grants all Roles that match. The Anonymous Role is always assigned, no matter what.
What ships in NodeOPCUA 2.174.0
1. The standard RoleSet with the well-known Roles
A vendor-neutral, predictable ladder of Roles that every conformant Server exposes:
- Anonymous, always assigned to every Session.
- AuthenticatedUser, anyone who provided valid credentials.
- TrustedApplication, any Client with a trusted app cert and a signed channel.
- Observer, read and subscribe.
- Operator, interact with the process.
- Engineer, configure and maintain.
- Supervisor, oversee operations.
- ConfigureAdmin, change server configuration.
- SecurityAdmin, change security configuration.
Plus AddRole / RemoveRole Methods to define custom Roles at runtime, so vendor-specific extensions name themselves into the standard model rather than living in a parallel proprietary scheme.
2. Identity mapping per Role
Identity mapping rules are managed live via AddIdentity / RemoveIdentity Methods. Nine criteria types are supported:
| Type | Matches |
|---|---|
| UserName | An OS or server-managed user name |
| Thumbprint | Hex thumbprint (upper-case) of a user Certificate |
| Role | A role entry from a JWT Access Token's roles array |
| GroupId | A group entry from a JWT Access Token's groups array (e.g. AD group) |
| Anonymous | Applies when no credentials are provided |
| AuthenticatedUser | Applies when any valid credentials are provided |
| Application | A specific ApplicationUri from a trusted Client Certificate |
| X509Subject | A structured X.509 subject name (e.g. CN="Jane Doe"/O="Sterfive"/C="FR") |
| TrustedApplication | Any trusted app cert over a signed channel |
The X509Subject matcher does proper structured comparison. Subjects are normalised into a fixed /-separated order (CN, O, OU, DC, L, S, C, dnQualifier, serialNumber), each value double-quoted, so matching is deterministic across certificate issuers.
3. Application and endpoint restrictions per Role
Two additional filters narrow Role grants beyond identity alone:
Applications: only specificApplicationUris qualify (allow-list), or all-except-listed qualify (deny-list, viaApplicationsExclude=TRUE).Endpoints: only Sessions connected through a specific endpoint URL, security mode, policy, or transport qualify, with the same allow/deny flip.
Typical use: grant Operator only when the Session connects through the encrypted production endpoint and the calling application matches the SCADA's ApplicationUri. Diagnostic-endpoint sessions never get Operator, even with the same user credentials.
4. User Management Methods
A standard local user store, exposed as a UserManagement Object under ServerConfiguration:
AddUser/ModifyUser/RemoveUser: admin-only, encrypted channel required.ChangePassword: callable by the user themselves, only if their token type isUSERNAME.
With per-user UserConfigurationMask flags: NoDelete, Disabled, NoChangeByUser, MustChangePassword. And a server-side PasswordOptionsMask that advertises and enforces complexity policy (RequiresUpperCase, RequiresLowerCase, RequiresDigit, RequiresSpecialCharacters).
The "must change password" flow is end-to-end. A flagged user activates a Session, gets back Good_PasswordChangeRequired, only holds the Anonymous Role until they call ChangePassword, then re-activates and receives their real Roles. ChangePassword is callable via well-known NodeIds even when not browseable, so a locked-out user can still reach it.
5. One-call wiring with createRoleBasedSecurity()
The whole RBAC machinery (users, roles, identity rules, application and endpoint filters, persistence) wires up in a single call. Persistence is optionally encrypted (AES-256-GCM) and survives restarts. The persistence archive is consolidated and install-order-independent, so you can rearrange your installRoleSet calls without rewriting state on disk.
6. Client API and role-set-admin CLI
A ClientRoleSet / ClientUserManagement API lets any OPC UA Client administer any conformant Server over OPC UA itself, with no out-of-band admin protocol.
Plus a ready-to-ship CLI:
npm i -g node-opcua-role-set-admin
The CLI prompts for passwords with hidden input, supports interactive admin sessions, and is fully programmable for CI/CD integration [7].
7. Defence-in-depth
- Sensitive
RoleSetandUserManagementnodes are hidden from non-admin Browse and require an encrypted channel. - Every successful
Add*/Remove*call raises aRoleMappingRuleChangedAuditEventTypeevent. The result is a tamper-evident trail of who changed which mapping, when. - Audit events never leak passwords.
- Disabling or removing a user closes all their Sessions and Subscriptions immediately.
- You cannot lock yourself out. Removing or disabling your own user fails with
Bad_InvalidSelfReference.
Setting it up: a working example
The simplest server with full Role-Based Security:
import {
OPCUAServer,
SecurityPolicy,
MessageSecurityMode,
} from "node-opcua";
import {
createRoleBasedSecurity,
WellKnownRoles,
} from "node-opcua-role-set-server";
async function main() {
const server = new OPCUAServer({
port: 4840,
resourcePath: "/UA/Plant",
securityPolicies: [SecurityPolicy.Basic256Sha256],
securityModes: [MessageSecurityMode.SignAndEncrypt],
});
await server.initialize();
// One call wires users, roles, identities, persistence.
await createRoleBasedSecurity(server, {
persistencePath: "./var/rbac",
encryptPersistence: true, // AES-256-GCM
users: [
{ username: "alice", password: "ChangeMe!" /*, roles auto-mapped */ },
{ username: "admin", password: "RotateMe1!", mustChangePassword: true },
],
identityRules: [
// alice => Operator
{ role: WellKnownRoles.Operator,
criteriaType: "UserName", criteria: "alice" },
// admin => SecurityAdmin (and AuthenticatedUser by default)
{ role: WellKnownRoles.SecurityAdmin,
criteriaType: "UserName", criteria: "admin" },
],
});
// Grant Operator + SecurityAdmin write on a setpoint;
// everyone else (Anonymous, AuthenticatedUser, Observer) gets read.
const ns = server.engine.addressSpace!.getOwnNamespace();
const setpoint = ns.addVariable({
browseName: "TemperatureSetpoint",
organizedBy: server.engine.addressSpace!.rootFolder.objects,
dataType: "Double",
rolePermissions: [
{ roleId: WellKnownRoles.Observer, permissions: "Read|Browse" },
{ roleId: WellKnownRoles.Operator, permissions: "Read|Browse|Write" },
{ roleId: WellKnownRoles.SecurityAdmin, permissions: "Read|Browse|Write" },
],
});
await server.start();
console.log(`Server ready at ${server.getEndpointUrl()}`);
}
main().catch(console.error);
That is the whole integration. Alice can read and write the setpoint; admin can do everything plus administer roles and users at runtime; an anonymous client can browse but not write; an authenticated user without an explicit Role grant can browse and read but not write.
Migrating from hand-rolled authorisation
If you have an existing NodeOPCUA server with your own userManager and per-node checks, the migration path:
- Keep your existing
userManagerduring the cutover.createRoleBasedSecurity()bridges to it transparently via theuserManageradapter. - Map your custom roles onto the well-known Roles where possible. A custom
"operator-line-A"becomes a custom Role added withAddRole, with identity rules pointing to the users who should hold it. - Replace per-node permission checks with
RolePermissionson each Node. The runtime will refuse Read / Write / Call / Browse calls that don't match, so there are noif (userManager.isAdmin(...))checks left in your code. - Move user storage into the standard
UserManagementstore, or keep your own and bridge throughIRoleResolver. - Once stable, retire the bridge and let
createRoleBasedSecurity()be the single source of truth.
Most projects can do this in half a day per server, plus a longer policy-review pass on each Node's RolePermissions.
What NodeOPCUA 2.174.0 ships against the OPC UA specification
The table below maps OPC 10000-18 (Part 18, Role-Based Security and User Management) and the relevant sections of OPC 10000-3 (Address Space Model) and OPC 10000-5 (Information Model) to what NodeOPCUA 2.174.0 implements out of the box. This is a self-conformance statement, not a competitive claim. Every entry can be verified against the OPC Foundation specifications.
| Spec capability | Source | NodeOPCUA 2.174.0 |
|---|---|---|
Standard RoleSet Object with the eight well-known Roles | OPC 10000-3 §8.4, Part 18 §4.2 | ✅ Full set: Anonymous, AuthenticatedUser, Observer, Operator, Engineer, Supervisor, ConfigureAdmin, SecurityAdmin |
AddRole / RemoveRole Methods on RoleSet | Part 18 §5.2 | ✅ Runtime, no restart |
Identity mapping with all nine IdentityCriteriaType values | Part 18 §6, Table 7 | ✅ Anonymous, AuthenticatedUser, UserName, Thumbprint, Role, GroupId, Application, X509Subject, TrustedApplication |
X509Subject normalisation for deterministic matching | Part 18 §6.2 | ✅ Canonical ordering CN, O, OU, DC, L, S, C, dnQualifier, serialNumber |
| Application and Endpoint restrictions per Role | Part 18 §5.3 | ✅ AddApplication, AddEndpoint, and corresponding removers |
UserManagement Object with AddUser / ModifyUser / RemoveUser / ChangePassword | Part 18 §7 | ✅ Including UserConfigurationMask and PasswordOptionsMask flag semantics |
| Encrypted persistence of the role and user store | Part 18 §8 (implementation-defined) | ✅ AES-256-GCM, key separation between data and metadata |
| Password hashing for the local user store | Part 18 §8 (implementation-defined) | ✅ Salted scrypt (N=2¹⁴, r=8, p=1) |
RoleMappingRuleChangedAuditEventType audit emission | Part 18 §9, Part 5 §6.4 | ✅ Emitted for every Role and identity mutation |
| Administration via OPC UA Methods rather than a side-channel protocol | Part 18 §5 | ✅ All admin surfaces are OPC UA Methods on the RoleSet / UserManagement Objects |
Defence-in-depth: sensitive Nodes hidden until SecurityAdmin, encrypted channel required for admin | Part 18 §5.4, Part 4 §7.1 | ✅ Enforced by access control on the relevant Nodes |
| CLI tooling for remote administration | Not specified | ✅ role-set-admin (acts as a standard OPC UA client) |
| Licence | Not specified | ✅ MIT (open source) |
Other OPC UA stacks implement the same specification. The value NodeOPCUA brings is that this full Part 18 surface is available in open source under MIT, with one-call setup, in a maintained release.
Where this fits in the bigger picture
OPC UA's security model has two operational pillars:
Pillar 1: trust at scale. Who is trusted to talk to whom. Centralised certificate issuance, renewal, revocation, and trust-list distribution. The Sterfive GDS Server (launching this month) is the commercial offering Sterfive provides here.
Pillar 2: authorisation at scale. Who can do what once trusted. Role-Based Security in NodeOPCUA 2.174.0 is exactly this pillar, shipping today in open source.
Together, trust and authorisation form the operational security layer that is built into the spec but, until recently, was almost always partially turned on or rolled by hand. With this release, the entire operational security model is now spec-conformant and out of the box for any team building on the NodeOPCUA stack.
For Node-RED based industrial deployments specifically, the same architecture is reachable through @opcua/for-node-red [8], which includes a built-in Certificate Manager UI and the GDS PULL Onboarding nodes (OPC UA Part 12 [9]).
What to do next
Try it locally in 5 minutes:
npm install node-opcua node-opcua-role-set-server
# copy the example above into server.ts, run it, connect with any OPC UA client
Read the spec primer (vendor-neutral, about 10 minutes): → OPC UA Part 18, Role-Based Security [4]
Read the project documentation (comprehensive API and architecture guide):
→ documentation/role_based_security.md [10]
See the full release notes: → NodeOPCUA v2.174.0 on GitHub [11]
Adopting at industrial scale? Sterfive offers architecture review, migration support, and enterprise SLA on NodeOPCUA. We are the team behind the stack; every line you run we wrote. → Book a 30-minute architecture briefing
References
[1] Bitsight TRACE. OPC UA Server Internet Exposures: 2025 Year in Review. Bitsight, 2025. https://www.bitsight.com/blog/opc-ua-server-internet-device-exposures-in-2025
[2] Erba, A., Müller, A., and Tippenhauer, N. O. Security Analysis of Vendor Implementations of the OPC UA Protocol for Industrial Control Systems. arXiv preprint 2104.06051 (2021); published at CPSIoTSec '22 (ACM, 2022). https://arxiv.org/abs/2104.06051
[3] Sterfive. The OPC UA Deployment Gap: a Four-Layer Defensible Security Architecture. White paper, 2025. https://www.sterfive.com/en/learn/opcua-deployment-gap
[4] Sterfive. OPC UA Part 18, Role-Based Security and User Management (TL;DR). https://www.sterfive.com/en/learn/opcua-reference/role-based-security
[5] OPC Foundation. OPC 10000-3: UA Part 3, Address Space Model. https://reference.opcfoundation.org/Core/Part3/
[6] OPC Foundation. OPC 10000-4: UA Part 4, Services. https://reference.opcfoundation.org/Core/Part4/
[7] NodeOPCUA. node-opcua-role-set-admin (npm package). https://www.npmjs.com/package/node-opcua-role-set-admin
[8] Sterfive. @opcua/for-node-red. https://www.sterfive.com/en/shop/opcua-for-nodered
[9] OPC Foundation. OPC 10000-12: UA Part 12, Discovery and Global Services. https://reference.opcfoundation.org/Core/Part12/
[10] NodeOPCUA. Project documentation: Role-Based Security. https://github.com/node-opcua/node-opcua/blob/master/documentation/role_based_security.md
[11] NodeOPCUA. Release notes for v2.174.0. https://github.com/node-opcua/node-opcua/releases/tag/v2.174.0
NodeOPCUA is the leading open-source OPC UA implementation for Node.js and TypeScript, with over 1.6k stars on GitHub and used in production across manufacturing, energy, water, and critical infrastructure. Maintained by Sterfive, the team behind it since 2014.