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 RoleSet with the well-known Roles (Anonymous, AuthenticatedUser, Operator, Observer, SecurityAdmin, ConfigureAdmin), plus AddRole / RemoveRole for custom Roles.
  • Identity mapping supports UserName, Thumbprint, full X.509 Distinguished-Name criteria, plus JWT Role / GroupId claims and Application / TrustedApplication mappings, all administered live via AddIdentity / 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:

QuestionTermWhere it's answered
"Who are you?"AuthenticationAt session creation (Part 4 [6]) via user identity tokens and application certificates
"What may you do?"AuthorizationPer-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:

TypeMatches
UserNameAn OS or server-managed user name
ThumbprintHex thumbprint (upper-case) of a user Certificate
RoleA role entry from a JWT Access Token's roles array
GroupIdA group entry from a JWT Access Token's groups array (e.g. AD group)
AnonymousApplies when no credentials are provided
AuthenticatedUserApplies when any valid credentials are provided
ApplicationA specific ApplicationUri from a trusted Client Certificate
X509SubjectA structured X.509 subject name (e.g. CN="Jane Doe"/O="Sterfive"/C="FR")
TrustedApplicationAny 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 specific ApplicationUris qualify (allow-list), or all-except-listed qualify (deny-list, via ApplicationsExclude=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 is USERNAME.

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 RoleSet and UserManagement nodes are hidden from non-admin Browse and require an encrypted channel.
  • Every successful Add* / Remove* call raises a RoleMappingRuleChangedAuditEventType event. 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:

  1. Keep your existing userManager during the cutover. createRoleBasedSecurity() bridges to it transparently via the userManager adapter.
  2. Map your custom roles onto the well-known Roles where possible. A custom "operator-line-A" becomes a custom Role added with AddRole, with identity rules pointing to the users who should hold it.
  3. Replace per-node permission checks with RolePermissions on each Node. The runtime will refuse Read / Write / Call / Browse calls that don't match, so there are no if (userManager.isAdmin(...)) checks left in your code.
  4. Move user storage into the standard UserManagement store, or keep your own and bridge through IRoleResolver.
  5. 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 capabilitySourceNodeOPCUA 2.174.0
Standard RoleSet Object with the eight well-known RolesOPC 10000-3 §8.4, Part 18 §4.2✅ Full set: Anonymous, AuthenticatedUser, Observer, Operator, Engineer, Supervisor, ConfigureAdmin, SecurityAdmin
AddRole / RemoveRole Methods on RoleSetPart 18 §5.2✅ Runtime, no restart
Identity mapping with all nine IdentityCriteriaType valuesPart 18 §6, Table 7Anonymous, AuthenticatedUser, UserName, Thumbprint, Role, GroupId, Application, X509Subject, TrustedApplication
X509Subject normalisation for deterministic matchingPart 18 §6.2✅ Canonical ordering CN, O, OU, DC, L, S, C, dnQualifier, serialNumber
Application and Endpoint restrictions per RolePart 18 §5.3AddApplication, AddEndpoint, and corresponding removers
UserManagement Object with AddUser / ModifyUser / RemoveUser / ChangePasswordPart 18 §7✅ Including UserConfigurationMask and PasswordOptionsMask flag semantics
Encrypted persistence of the role and user storePart 18 §8 (implementation-defined)✅ AES-256-GCM, key separation between data and metadata
Password hashing for the local user storePart 18 §8 (implementation-defined)✅ Salted scrypt (N=2¹⁴, r=8, p=1)
RoleMappingRuleChangedAuditEventType audit emissionPart 18 §9, Part 5 §6.4✅ Emitted for every Role and identity mutation
Administration via OPC UA Methods rather than a side-channel protocolPart 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 adminPart 18 §5.4, Part 4 §7.1✅ Enforced by access control on the relevant Nodes
CLI tooling for remote administrationNot specifiedrole-set-admin (acts as a standard OPC UA client)
LicenceNot 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:

A defensible OPC UA security architecture in four layers: asset owners, corporate Global Discovery Server, lifecycle automation, policy plane

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.