TL;DR: OPC UA Part 18: Role-Based Security

Full spec: reference.opcfoundation.org/Core/Part18 Β· Version 1.05.06 Β· Published 2025-10-22

NodeOPCUA Implementation: For a practical guide to implementing this specification in Node.js/TypeScript using NodeOPCUA 2.174.0, including a working 30-line code example and a details matrix of Part 18 spec-conformance, see the blog post OPC UA Role-Based Security in Practice.

What Is It?

OPC UA Part 18 defines the Information Model for role-based security, which is the standard way a Server decides who is allowed to do what in its AddressSpace.

It does not invent new permissions (those live in Part 3). Instead it defines the plumbing that connects identities (who is connecting) to Roles (named buckets of privileges), plus a standard API to manage local users.

Think of it as the "RBAC engine" of OPC UA: the same idea as Unix groups, Windows security groups, or cloud IAM roles, applied to an OPC UA Server.

flowchart LR
    ID["πŸ‘€ Identity<br/>(user token + app cert<br/>+ endpoint)"]
    ROLE["🎭 Role<br/>(Operator, Engineer,<br/>SecurityAdmin...)"]
    PERM["πŸ”‘ Permissions<br/>(Read, Write, Call,<br/>Browse... per Node)"]

    ID -->|"mapping rules<br/>(Part 18)"| ROLE
    ROLE -->|"RolePermissions<br/>(Part 3)"| PERM

    style ID fill:#1d3557,color:#fff
    style ROLE fill:#2d6a4f,color:#fff
    style PERM fill:#6a040f,color:#fff

The Big Idea: Separate Authentication from Authorization

The whole point of Part 18 is to decouple two questions that are easy to tangle:

QuestionTermWho answers itWhere
"Who are you?"AuthenticationUser token + application certificateSession creation (Part 4)
"What may you do?"AuthorizationPermissions granted to your Role(s)Per-Node (Part 3)

By splitting them, a Server can let a centralized service manage user identities and credentials, while the Server itself only ever manages Permissions for Roles on its Nodes. Add a new engineer to the directory β†’ they automatically get the Engineer Role β†’ they inherit every permission that Role was granted. The Server never stores that person's password.


How a Session Gets Its Roles

When a Client activates a Session, the Server evaluates every Role's mapping rules against the Session's properties and grants all Roles that match.

flowchart TB
    START["Session activated"]
    START --> CHECK{"For each Role:<br/>do ALL conditions match?"}

    CHECK --> C1["β‘  UserIdentityToken<br/>complies with<br/><b>Identities</b> rules"]
    CHECK --> C2["β‘‘ Client Certificate<br/>complies with<br/><b>Applications</b> filter"]
    CHECK --> C3["β‘’ Endpoint used<br/>complies with<br/><b>Endpoints</b> filter"]

    C1 --> GRANT
    C2 --> GRANT
    C3 --> GRANT
    GRANT{"All three true?"}
    GRANT -->|Yes| Y["βœ… Role granted<br/>to Session"]
    GRANT -->|No| N["β›” Role not granted"]

    style Y fill:#2d6a4f,color:#fff
    style N fill:#6a040f,color:#fff

Always-on default: the Anonymous Role is assigned to every Session, no matter what. If a Role's Identities list is empty and CustomConfiguration is not TRUE, that Role can never be granted to anyone.

Re-evaluation: if a Role's configuration changes while Sessions are active, the Server re-evaluates and re-applies Role assignments to those Sessions immediately.


The Role Model: Information Model Map

flowchart TB
    SC["ServerCapabilities<br/>(Part 5)"]
    SC -->|HasComponent| RS["RoleSet : RoleSetType"]

    RS -->|"AddRole()"| ADD["βž• add a Role"]
    RS -->|"RemoveRole()"| REM["βž– remove a Role"]
    RS -->|HasComponent| R1["Operator : RoleType"]
    RS -->|HasComponent| R2["Engineer : RoleType"]
    RS -->|HasComponent| R3["SecurityAdmin : RoleType"]
    RS -.->|"... + well-known Roles"| DOTS["..."]

    R1 --> PROPS["Properties + Methods<br/>(see RoleType below)"]

    style RS fill:#2d6a4f,color:#fff
    style PROPS fill:#1d3557,color:#fff

RoleSetType: the container of Roles

The RoleSet Object lives under Server.ServerCapabilities and publishes all Roles the Server supports.

MemberKindPurpose
<RoleName>Object (RoleType)One per Role, acting as a placeholder for any number
AddRole(RoleName, NamespaceUri) β†’ RoleNodeIdMethodConfiguration Clients add a new Role
RemoveRole(RoleNodeId)MethodRemove a Role (deletes all its Permissions)

⚠️ Security gate on every admin Method in Part 18: the Client shall use an encrypted channel and present credentials with admin rights (e.g. the SecurityAdmin Role). Otherwise β†’ Bad_SecurityModeInsufficient or Bad_UserAccessDenied.


The Well-Known Roles

Part 18 ships a standard ladder of Roles (NodeIds defined in Part 6). Servers should support them so Clients get predictable behavior across vendors.

flowchart TB
    A["Anonymous<br/><i>always assigned</i>"]
    B["AuthenticatedUser<br/><i>any valid credential</i>"]
    T["TrustedApplication<br/><i>trusted app cert + signed channel</i>"]
    O["Observer: read and subscribe"]
    OP["Operator: interact with process"]
    E["Engineer: configure and maintain"]
    S["Supervisor: oversee operations"]
    CA["ConfigureAdmin: change server config"]
    SA["SecurityAdmin: change security config"]

    A --> B --> O --> OP --> E --> S
    B --> T
    E --> CA
    CA --> SA

    style A fill:#495057,color:#fff
    style SA fill:#6a040f,color:#fff
    style CA fill:#9d0208,color:#fff
RoleDefault Identity criteriaTypical privilege
AnonymousAnonymous + AuthenticatedUserDefault for all Sessions
AuthenticatedUserAuthenticatedUserAnyone who provided valid credentials
TrustedApplicationTrustedApplicationAny Client with a trusted app cert + signed channel
Observer(configured)Read values, browse, subscribe
Operator(configured)Operate the process (write setpoints, ack alarms)
Engineer(configured)Engineering / maintenance changes
Supervisor(configured)Supervisory oversight
ConfigureAdmin(configured)Modify server configuration
SecurityAdmin(configured)Modify security configuration

πŸ”’ Immutable trio: a Server shall not allow changes to Anonymous, AuthenticatedUser, or TrustedApplication, and shall not allow deletion of Anonymous / AuthenticatedUser / TrustedApplication.


RoleType: Anatomy of a Role

Each Role Object is a RoleType with the following Properties and configuration Methods. All of it is sensitive, meaning it is browseable, readable, writeable, and callable only by authorized admins over an encrypted channel.

Role : RoleType
β”œβ”€β”€ Identities          IdentityMappingRuleType[]   ← WHO maps to this Role   (Mandatory)
β”œβ”€β”€ ApplicationsExclude Boolean                      ← treat Applications as deny-list? (Optional)
β”œβ”€β”€ Applications        String[]   (ApplicationUris) ← WHICH client apps      (Optional)
β”œβ”€β”€ EndpointsExclude    Boolean                      ← treat Endpoints as deny-list?    (Optional)
β”œβ”€β”€ Endpoints           EndpointType[]               ← WHICH endpoints        (Optional)
β”œβ”€β”€ CustomConfiguration Boolean                      ← vendor-specific mapping?(Optional)
β”‚
β”œβ”€β”€ AddIdentity(Rule)         RemoveIdentity(Rule)        ← manage identity rules
β”œβ”€β”€ AddApplication(AppUri)    RemoveApplication(AppUri)   ← manage app filter
└── AddEndpoint(Endpoint)     RemoveEndpoint(Endpoint)    ← manage endpoint filter

The three filters, and how *Exclude flips them

Applications and Endpoints are filter lists whose meaning is flipped by their companion *Exclude flag:

*Exclude valueList behaves asMeaning
FALSE (or absent)Include / allow-listOnly items in the list qualify for the Role
TRUEExclude / deny-listEverything except the list qualifies

Default *Exclude is TRUE when the list is empty (so an empty list = "everyone qualifies"). If Applications has any entry, the Role is only granted over a signed (or signed-and-encrypted) channel.

✍️ Configuration rule: Identities, Applications, and Endpoints are read-only via the Write Service (CurrentWrite is FALSE); they must be changed through the Add*/Remove* Methods. Only ApplicationsExclude / EndpointsExclude are set with plain Writes.

CustomConfiguration: the escape hatch

If a Server wants to support the RolePermissions Attribute but can't implement the standard mapping machinery, it sets CustomConfiguration = TRUE. It may then hide the standard config or expose vendor-specific options. Roles are required to support RolePermissions; this flag is how a Server says "the mapping is mine to manage."


Identity Mapping Rules: the Heart of "Who Maps to What"

An IdentityMappingRuleType is a single (criteriaType, criteria) rule. A Role's Identities Property is an array of these.

IdentityMappingRuleType {
    criteriaType : IdentityCriteriaType   ← what kind of match
    criteria     : String                 ← the value to match (null/empty for the 3 "any" types)
}

The IdentityCriteriaType enumeration

flowchart LR
    subgraph who["Match a specific person"]
        UN["UserName (1)<br/>OS or server-managed user"]
        TP["Thumbprint (2)<br/>user cert thumbprint (hex)"]
        X5["X509Subject (8)<br/>cert subject / issuer name"]
    end
    subgraph token["Match a claim in an Access Token"]
        RO["Role (3)<br/>role claim in JWT"]
        GR["GroupId (4)<br/>group claim in JWT"]
    end
    subgraph any["Match a category"]
        AN["Anonymous (5)<br/>no credentials"]
        AU["AuthenticatedUser (6)<br/>any valid credentials"]
        AP["Application (7)<br/>specific ApplicationUri"]
        TA["TrustedApplication (9)<br/>any trusted app cert"]
    end
ValuecriteriaTypeWhat criteria holds
1UserNameA user name known to the Server (OS account or server-managed, see Β§5)
2ThumbprintHex thumbprint (upper-case, no spaces) of a user Certificate
3RoleA role entry from the Access Token (roles array of a JWT; prefixed by iss/ if present)
4GroupIdA group entry from the Access Token (groups array of a JWT; e.g. AD security group)
5Anonymous(empty), applies when no credentials were provided
6AuthenticatedUser(empty), applies when any valid credentials were provided
7ApplicationAn ApplicationUri from a trusted Client Certificate (signed channel required)
8X509SubjectA structured subject name, e.g. CN="User Name"/O="Company"
9TrustedApplication(empty), any trusted app cert over a signed/encrypted channel

πŸ›‘οΈ Sane-default guidance: the Server should refuse dangerous combinations, such as adding an Anonymous (5) mapping rule to an administrator Role. AddIdentity may return Bad_RequestNotAllowed for exactly this reason.

X509Subject ordering (Table 10)

When matching X509Subject, the subject name is normalized into a fixed /-separated order, each value double-quoted:

CN β†’ O β†’ OU β†’ DC β†’ L β†’ S β†’ C β†’ dnQualifier β†’ serialNumber

Only these names are compared; others are ignored. Repeated names appear in certificate order. Example: CN="Jane Doe"/O="Sterfive"/C="FR".


EndpointType: Filtering by Connection

Endpoints entries are EndpointType structures. Only non-default fields are compared, so you can match loosely (just a URL) or tightly (URL + security mode + policy + transport).

EndpointType {
    endpointUrl         String                ← the endpoint URL
    securityMode        MessageSecurityMode   ← default Invalid β†’ ignored if default
    securityPolicyUri   String                ← default empty   β†’ ignored if default
    transportProfileUri String                ← default empty   β†’ ignored if default
}

Use case: grant Operator only when the Session connects through the encrypted production endpoint, never through a diagnostic one.


Auditing Role Changes

Every successful Add*/Remove* call on a Role raises a RoleMappingRuleChangedAuditEventType (abstract subtype of AuditUpdateMethodEventType). This gives you a tamper-evident trail of who changed which mapping rule, when, which is essential for security compliance.

sequenceDiagram
    participant Admin as πŸ› οΈ SecurityAdmin
    participant Srv as πŸ–₯️ Server
    participant Sub as πŸ“‹ Audit Subscriber

    Admin->>Srv: AddIdentity(Operator, {UserName,"jdoe"})<br/>(encrypted channel)
    Srv->>Srv: Update Operator.Identities
    Srv->>Srv: Re-evaluate active Sessions
    Srv-->>Sub: RoleMappingRuleChangedAuditEvent
    Note over Sub: who / when / which method / which Role

User Management Model (Β§5): Local Users for UserName Rules

The UserName criteriaType needs the Server to verify a name + password locally. Part 18 Β§5 defines a standard API to manage that user list, exposed as a UserManagement Object under ServerConfiguration (from Part 12).

ServerConfiguration (Part 12)
└── UserManagement : UserManagementType
    β”œβ”€β”€ Users               UserManagementDataType[]   ← the user list (admin-only)   (Mandatory)
    β”œβ”€β”€ PasswordLength       Range                       ← min/max length (0 = no limit)(Mandatory)
    β”œβ”€β”€ PasswordOptions      PasswordOptionsMask         ← server password features     (Mandatory)
    β”œβ”€β”€ PasswordRestrictions LocalizedText               ← human-readable rules         (Optional)
    β”‚
    β”œβ”€β”€ AddUser(UserName, Password, UserConfiguration, Description)        ← admin
    β”œβ”€β”€ ModifyUser(UserName, ModifyPassword, Password, ...)               ← admin
    β”œβ”€β”€ RemoveUser(UserName)                                              ← admin
    └── ChangePassword(OldPassword, NewPassword)                         ← the user themselves

Who can call what

MethodCallerNotes
AddUser / ModifyUser / RemoveUserAdmin (e.g. SecurityAdmin)Encrypted channel; sensitive
ChangePasswordThe Session userOnly if the user token type is USERNAME; encrypted channel

Removing or disabling a user closes all their Sessions and Subscriptions. You cannot remove or disable yourself; that action fails with Bad_InvalidSelfReference.

UserConfigurationMask: per-user flags

BitFlagMeaning
0NoDeleteUser cannot be deleted
1DisabledUser behaves as non-existent for ActivateSession
2NoChangeByUserUser cannot change their own password
3MustChangePasswordUser must change password before getting Roles (invalid together with NoChangeByUser)

PasswordOptionsMask: server password policy

Advertises what the Server supports / requires: initial-password-change, disable-user, no-delete, no-change, descriptions, and complexity rules (RequiresUpperCase, RequiresLowerCase, RequiresDigit, RequiresSpecialCharacters). If the Server has no special requirements, all bits are FALSE.

The "must change password" dance

sequenceDiagram
    participant C as πŸ‘€ Client
    participant S as πŸ–₯️ Server

    C->>S: ActivateSession(user, oldPassword)
    S-->>C: Good_PasswordChangeRequired
    Note over C,S: Session has ONLY the Anonymous Role for now

    C->>S: ChangePassword(oldPassword, newPassword)
    S-->>C: Good  (MustChangePassword β†’ FALSE)

    C->>S: ActivateSession(user, newPassword)
    S-->>C: Good  βœ… now gets the user's real Roles

ChangePassword is callable via well-known NodeIds even when it isn't browseable for that user, so a locked-out, must-change user can still reach it.


Putting It All Together: End-to-End

flowchart TB
    subgraph connect["1 Β· Connect & Authenticate (Part 4)"]
        U["πŸ‘€ User token<br/>(UserName / Cert / JWT)"]
        AC["πŸ“œ Application cert"]
        EP["πŸ”Œ Endpoint"]
    end

    subgraph map["2 Β· Map to Roles (Part 18)"]
        RULES["Evaluate every Role's<br/>Identities + Applications + Endpoints"]
    end

    subgraph roles["3 Β· Roles granted"]
        GR["🎭 e.g. {Anonymous, Operator}"]
    end

    subgraph authz["4 Β· Authorize each request (Part 3)"]
        RP["RolePermissions on the target Node<br/>β†’ Read? Write? Call? Browse?"]
    end

    connect --> RULES --> GR --> RP
    RP -->|granted| OK["βœ… Service succeeds"]
    RP -->|denied| NO["β›” Bad_UserAccessDenied"]

    style GR fill:#2d6a4f,color:#fff
    style OK fill:#2d6a4f,color:#fff
    style NO fill:#6a040f,color:#fff

Key Takeaways

  1. Part 18 = the RBAC engine: it maps identities β†’ Roles; Part 3 maps Roles β†’ Permissions per Node. Two halves of one access-control story.
  2. Authentication β‰  Authorization: splitting them lets a Server delegate identity to a central service while owning only its Node Permissions.
  3. A Session can hold many Roles: granted when all three filters (Identities and Applications and Endpoints) match. Anonymous is always there.
  4. Mapping rules are configured by Methods, not Writes: AddIdentity/AddApplication/AddEndpoint, all admin-only over encrypted channels.
  5. *Exclude flips lists between allow-list and deny-list, and an empty list defaults to "everyone."
  6. Nine ways to identify a caller: from a literal UserName to JWT Role/GroupId claims to X509Subject matching.
  7. Well-known Roles are a portable ladder: Observer β†’ Operator β†’ Engineer β†’ Supervisor β†’ ConfigureAdmin β†’ SecurityAdmin; the Anonymous/AuthenticatedUser/TrustedApplication trio is immutable.
  8. Every change is audited via RoleMappingRuleChangedAuditEventType.
  9. Β§5 adds a standard local user store: AddUser/ModifyUser/RemoveUser/ChangePassword, with password policy flags and a "must change password" flow.
  10. You can't lock yourself out: disabling or removing your own user fails with Bad_InvalidSelfReference.

Relationship to Other Parts

flowchart TB
    P1["Part 1: Concepts"]
    P3["Part 3: Address Space<br/>Permissions, RolePermissions Attr"]
    P4["Part 4: Services<br/>Sessions, user tokens, MessageSecurityMode"]
    P5["Part 5: Information Model<br/>BaseObjectType, ServerCapabilities, RoleSet"]
    P6["Part 6: Mappings<br/>well-known Role NodeIds, JWT→token groups"]
    P8["Part 8: Data Access<br/>Range DataType"]
    P12["Part 12: Discovery / GDS<br/>ServerConfiguration Object"]
    P18["Part 18: Role-Based Security<br/>RoleSet, RoleType, mapping rules,<br/>UserManagement"]

    P18 -->|grants Permissions defined by| P3
    P18 -->|consumes Sessions/tokens from| P4
    P18 -->|RoleSet hangs off ServerCapabilities| P5
    P18 -->|uses well-known NodeIds + JWT rules| P6
    P18 -->|PasswordLength uses Range| P8
    P18 -->|UserManagement hangs off ServerConfiguration| P12

    style P18 fill:#2d6a4f,color:#fff
    style P3 fill:#1d3557,color:#fff
PartWhat Part 18 borrows from it
Part 3The actual Permissions and the RolePermissions Node Attribute that Roles are granted
Part 4Session/ActivateSession, user identity tokens, MessageSecurityMode
Part 5BaseObjectType, ServerCapabilities (host of RoleSet), AuditUpdateMethodEventType
Part 6Well-known Role NodeIds; how AD groups/roles are added to Access Tokens (JWT)
Part 8Range DataType (used by PasswordLength)
Part 12ServerConfiguration Object (host of UserManagement)

TL;DR generated from OPC 10000-18 v1.05.06 (2025-10-22). For normative wording, including exact result codes, conformance units, and table definitions, always consult the official specification.