OPC Studio User's Guide and Reference
OPC UA User Authentication (Client)
Fundamentals > Common Fundamentals > Security > OPC UA Client-Server Security > OPC UA User Authentication (Client)
In This Topic

Introduction

This article describes how you can provide the user identity that your OPC UA client application will use to connect to the OPC UA server. Whether or not this user authentication is needed depends on how the OPC UA server is set up. Depending on whether or not you are providing the user identity, and depending on which concrete user you specify, the OPC UA server may allow you to perform different set of operations, or it may reject the connection altogether.

In your application based on OPC Studio, you specify the user inside the EasyUAClient object parameters (see User Identity in QuickOPC-UA), or as part of the OPC UA endpoint descriptor (UAEndpointDescriptor Class). The main part of the endpoint descriptor is the endpoint URL, but there are other parts that can be specified as well, and one of them is the UserIdentity Property. This property contains a user identity object, which is an instance of the UserIdentity Class

The user identity object acts as a collection of token infos. The token info specifies the user to be authenticated using a particular approach, such as anonymous, user name, Kerberos (issued), or X.509 certificate. Each token info can be either configured, or not. Token infos that are not configured are ignored for user authentication. When OPC Data Client connects to the OPC UA server, it matches the configured token infos with the token types supported by the server endpoint, and finds the best match. If no match is found, no user token is passed to the server, which usually results in an error. 

Most often, you will configure one user token in the user identity. If you configure multiple user tokens in the user identity, they should logically correspond to the same user - just specified in different ways. This way, the proper user token can be chosen when connecting to different servers or endpoints - but there will no ambiguity as to which user is actually connecting.

Manipulating this object in the endpoint descriptor that you use to connect to the OPC UA server gives you a way to specify the user identity. It can be done by setting the various properties and sub-properties. Alternatively, in .NET languages, OPC Data Client gives you extension methods (on the UAEndpointDescriptor Class) that allow you to create an endpoint descriptor with a chosen token info type using a single method call. These extension methods are called WithXXXXIdentity, where XXXX is the type of the token info to be used. 

Do not attempt to use the UserName PropertyPassword Property or UserInfo Property of the UAEndpointDescriptor Class (i.e. the properties directly on the endpoint descriptor object) to specify the user identity. It will not work. These properties correspond to the user info subcomponent of the URL, and are not normally used in OPC UA. Always use the UserIdentity Property, or designated extension methods, for user authentication in OPC UA.

Anonymous token

Anonymous token in the user identity is represented by the AnonymousTokenInfo Class object in the UserIdentity.AnonymousTokenInfo Property, and is configured by default. You can control whether or not it is configured by manipulating the UserIdentity.AnonymousTokenInfo.IsEnabled Property.

With many servers, it does not make any difference whether an anonymous token is specified or not, and if there is no user token, the behavior is the same as if the anonymous token was specified.

Kerberos (issued) token

This functionality is not available under (or the text does not apply to) .NET 6+ development platform.

In Windows environments, this token type allows you to use the network credentials for OPC UA user authentication. Either a custom network credential, or the current user's identity can be used. The custom network credential specifies the user name, password, and domain.

Kerberos (issued) token in the user identity is represented by the KerberosTokenInfo Class object in the UserIdentity.KerberosTokenInfo Property. The actual user that you want to authenticate is specified by the NetworkSecurity object in the UserIdentity.KerberosTokenInfo.NetworkSecurity Property.

In .NET languages, instead of manipulating the object properties, you can take an OPC UA endpoint descriptor, and create a new endpoint descriptor from it, with user identity provided by the Kerberos (issued) token. To do so, use the WithKerberosIdentity Method.

User name token

The user name token uses a user name (string) and an optional password (a string) to identify and authenticate the user. The user name token is represented by the UserNameTokenInfo Class object in the UserIdentity.UserNameTokenInfo Property. The user name is contained in the UserName Property and the password in the Password Property of the UserNameTokenInfo Class. The user name token info is considered configured when the user name or the password is not empty.

In .NET languages, instead of manipulating the object properties, you can take an OPC UA endpoint descriptor, and create a new endpoint descriptor from it, with user identity provided by the user name token. To do so, use the WithUserNameIdentity Method.

Example

.NET

// Shows how to find all registrations in the GDS.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.Discovery;
using OpcLabs.EasyOpc.UA.Extensions;
using OpcLabs.EasyOpc.UA.Gds;
using OpcLabs.EasyOpc.UA.OperationModel;

namespace UADocExamples.Gds._EasyUAGlobalDiscoveryClient
{
    class FindApplications
    {
        public static void Main1()
        {
            // Define which GDS we will work with.
            UAEndpointDescriptor gdsEndpointDescriptor =
                ((UAEndpointDescriptor)"opc.tcp://opcua.demo-this.com:58810/GlobalDiscoveryServer")
                .WithUserNameIdentity("appuser", "demo");

            // Instantiate the global discovery client object
            var globalDiscoveryClient = new EasyUAGlobalDiscoveryClient();

            // Find all (client or server) applications registered in the GDS.
            UAApplicationDescription[] applicationDescriptionArray;
            try
            {
                globalDiscoveryClient.QueryApplications(
                    gdsEndpointDescriptor: gdsEndpointDescriptor,
                    startingRecordId: 0,
                    maximumRecordsToReturn: 0,
                    applicationName: "",
                    applicationUriString: "",
                    applicationTypes: UAApplicationTypes.All,
                    productUriString: "",
                    serverCapabilities: new string[0],
                    lastCounterResetTime: out _,
                    nextRecordId: out _,
                    applications: out applicationDescriptionArray);
            }
            catch (UAException uaException)
            {
                Console.WriteLine("*** Failure: {0}", uaException.GetBaseException().Message);
                return;
            }

            // For each application returned by the query, find its registrations in the GDS.
            foreach (UAApplicationDescription applicationDescription in applicationDescriptionArray)
            {
                Console.WriteLine();
                Console.WriteLine("Application URI string: {0}", applicationDescription.ApplicationUriString);

                UAApplicationRecordDataType[] applicationRecordArray;
                try
                {
                    applicationRecordArray = globalDiscoveryClient.FindApplications(
                        gdsEndpointDescriptor,
                        applicationDescription.ApplicationUriString);
                }
                catch (UAException uaException)
                {
                    Console.WriteLine("  *** Failure: {0}", uaException.GetBaseException().Message);
                    continue;
                }

                // Display results
                foreach (UAApplicationRecordDataType applicationRecord in applicationRecordArray)
                    Console.WriteLine("  Application ID: {0}", applicationRecord.ApplicationId);
            }


            // Example output:
            //
            //Application URI string: urn:sampleserver
            //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=09ecaa08-6ec6-462c-a214-1e66a3099107
            //
            //Application URI string: urn:alarmconditionserver
            //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=783e1e9a-8036-43b6-928f-97488c460266
            //
            //Application URI string: urn:PC:MultiTargetUADocExamples:5.54.1026.1:neutral:null
            //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=9e700ea5-55a6-4c3c-ba9f-b91c890dc519
            //
            //Application URI string: urn:PC:UADocExamples:5.56.0.16:neutral:null
            //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=e182e28c-086b-4fc7-82c7-70ca7cda3033
            //
            //Application URI string: urn:PC:cscript:5.812.10240.16384
            //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=aec94459-f513-4979-8619-8383555fca61
        }
    }
}
' Shows how to find all registrations in the GDS.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.Discovery
Imports OpcLabs.EasyOpc.UA.Extensions
Imports OpcLabs.EasyOpc.UA.Gds
Imports OpcLabs.EasyOpc.UA.OperationModel

Namespace Gds._EasyUAGlobalDiscoveryClient
    Friend Class FindApplications
        Public Shared Sub Main1()

            ' Define which GDS we will work with.
            Dim gdsEndpointDescriptor As UAEndpointDescriptor =
                New UAEndpointDescriptor("opc.tcp://opcua.demo-this.com:58810/GlobalDiscoveryServer") _
                .WithUserNameIdentity("appuser", "demo")

            ' Instantiate the global discovery client object
            Dim globalDiscoveryClient = New EasyUAGlobalDiscoveryClient()

            ' Find all (client or server) applications registered in the GDS.
            Dim applicationDescriptionArray() As UAApplicationDescription = Nothing
            Try
                Dim lastCounterResetTime As DateTime
                Dim nextRecordId As Long
                globalDiscoveryClient.QueryApplications(
                    gdsEndpointDescriptor:=gdsEndpointDescriptor,
                    startingRecordId:=0,
                    maximumRecordsToReturn:=0,
                    applicationName:="",
                    applicationUriString:="",
                    applicationTypes:=UAApplicationTypes.All,
                    productUriString:="",
                    serverCapabilities:=New String() {},
                    lastCounterResetTime:=lastCounterResetTime,
                    nextRecordId:=nextRecordId,
                    applications:=applicationDescriptionArray)
            Catch uaException As UAException
                Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message)
                Exit Sub
            End Try

            ' For each application returned by the query, find its registrations in the GDS.
            For Each applicationDescription As UAApplicationDescription In applicationDescriptionArray
                Console.WriteLine()
                Console.WriteLine("Application URI string: {0}", applicationDescription.ApplicationUriString)

                Dim applicationRecordArray() As UAApplicationRecordDataType
                Try
                    applicationRecordArray = globalDiscoveryClient.FindApplications(
                        gdsEndpointDescriptor,
                        applicationDescription.ApplicationUriString)
                Catch uaException As UAException
                    Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message)
                    Continue For
                End Try

                ' Display results
                For Each applicationRecord As UAApplicationRecordDataType In applicationRecordArray
                    Console.WriteLine("  Application ID: {0}", applicationRecord.ApplicationId)
                Next applicationRecord
            Next applicationDescription
        End Sub
    End Class
End Namespace

COM

// Shows how to find all registrations in the GDS.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client and subscriber examples in Object Pascal (Delphi) on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-OP .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

class procedure FindApplications.Main;
var
  ApplicationDescription: _UAApplicationDescription;
  ApplicationDescriptionArray: OleVariant;
  ApplicationName: WideString;
  ApplicationRecord: _UAApplicationRecordDataType;
  ApplicationRecordArray: OleVariant;
  ApplicationUriString: WideString;
  GlobalDiscoveryClient: OpcLabs_EasyOpcUA_TLB._EasyUAGlobalDiscoveryClient;
  GdsEndpointDescriptor: _UAEndpointDescriptor;
  I, J: integer;
  LastCounterResetTime: TDateTime;
  MaximumRecordsToReturn: Integer;
  NextRecordId: Integer;
  ProductUriString: WideString;
  ServerCapabilities: array of string;
  StartingRecordId: Integer;
begin
  // Define which GDS we will work with.
  GdsEndpointDescriptor := CoUAEndpointDescriptor.Create;
  GdsEndpointDescriptor.UrlString := 'opc.tcp://opcua.demo-this.com:58810/GlobalDiscoveryServer';
  GdsEndpointDescriptor.UserIdentity.UserNameTokenInfo.UserName := 'appadmin';
  GdsEndpointDescriptor.UserIdentity.UserNameTokenInfo.Password := 'demo';

  // Instantiate the global discovery client object
  GlobalDiscoveryClient := CoEasyUAGlobalDiscoveryClient.Create;

  // Find all (client or server) applications registered in the GDS.
  StartingRecordId := 0;
  MaximumRecordsToReturn := 0;
  ApplicationName := '';
  ApplicationUriString := '';
  ProductUriString := '';
  try
    GlobalDiscoveryClient.QueryApplications(
      GdsEndpointDescriptor,
      StartingRecordId,
      MaximumRecordsToReturn,
      ApplicationName,
      ApplicationUriString,
      UAApplicationTypes_All,
      ProductUriString,
      ServerCapabilities,
      LastCounterResetTime,
      NextRecordId,
      ApplicationDescriptionArray);
  except
    on E: EOleException do
    begin
      WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message]));
    end;
  end;

  // For each application returned by the query, find its registrations in the GDS.
  for I := VarArrayLowBound(ApplicationDescriptionArray,1) to VarArrayHighBound(ApplicationDescriptionArray,1) do
  begin
    ApplicationDescription := IUnknown(ApplicationDescriptionArray[I]) as _UAApplicationDescription;
    WriteLn;
    WriteLn('Application URI string: ', ApplicationDescription.ApplicationUriString);
    try
    TVarData(ApplicationRecordArray).VType := varArray or varVariant;
    TVarData(ApplicationRecordArray).VArray := PVarArray(
      GlobalDiscoveryClient.FindApplications(
        GdsEndpointDescriptor,
        ApplicationDescription.ApplicationUriString));
    except
      on E: EOleException do
      begin
        WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message]));
        Continue;
      end;
    end;
      for J := VarArrayLowBound(ApplicationRecordArray, 1) to VarArrayHighBound(ApplicationRecordArray, 1) do
      begin
        // Display results
        ApplicationRecord := IUnknown(ApplicationRecordArray[J]) as _UAApplicationRecordDataType;
        WriteLn('  Application ID: ', ApplicationRecord.ApplicationId.ToString);
      end;
  end;

  // Example output:
  //
  //Application URI string: urn:sampleserver
  //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=09ecaa08-6ec6-462c-a214-1e66a3099107
  //
  //Application URI string: urn:alarmconditionserver
  //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=783e1e9a-8036-43b6-928f-97488c460266
  //
  //Application URI string: urn:PC:MultiTargetUADocExamples:5.54.1026.1:neutral:null
  //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=9e700ea5-55a6-4c3c-ba9f-b91c890dc519
  //
  //Application URI string: urn:PC:UADocExamples:5.56.0.16:neutral:null
  //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=e182e28c-086b-4fc7-82c7-70ca7cda3033
  //
  //Application URI string: urn:PC:cscript:5.812.10240.16384
  //  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=aec94459-f513-4979-8619-8383555fca61

end;
Rem Shows how to find all registrations in the GDS.
Rem
Rem Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
Rem OPC client and subscriber examples in VBScript on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBScript .
Rem Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
Rem a commercial license in order to use Online Forums, and we reply to every post.

Option Explicit

Const UAApplicationTypes_All = 7

' Define which GDS we will work with.
Dim GdsEndpointDescriptor: Set GdsEndpointDescriptor = CreateObject("OpcLabs.EasyOpc.UA.UAEndpointDescriptor")
GdsEndpointDescriptor.UrlString = "opc.tcp://opcua.demo-this.com:58810/GlobalDiscoveryServer"
GdsEndpointDescriptor.UserIdentity.UserNameTokenInfo.UserName = "appadmin"
GdsEndpointDescriptor.UserIdentity.UserNameTokenInfo.Password = "demo"

' Instantiate the global discovery client object
Dim GlobalDiscoveryClient: Set GlobalDiscoveryClient = CreateObject("OpcLabs.EasyOpc.UA.Gds.EasyUAGlobalDiscoveryClient")

' Find all (client or server) applications registered in the GDS.
Dim startingRecordId: startingRecordId = 0
Dim maximumRecordsToReturn: maximumRecordsToReturn = 0
Dim applicationName: applicationName = ""
Dim applicationUriString: applicationUriString = ""
Dim productUriString: productUriString = ""
Dim serverCapabilities: serverCapabilities = Array()
Dim lastCounterResetTime
Dim nextRecordId
Dim applicationDescriptionArray
On Error Resume Next
GlobalDiscoveryClient.QueryApplications _
    GdsEndpointDescriptor, _
    startingRecordId, _
    maximumRecordsToReturn, _
    applicationName, _
    applicationUriString, _
    UAApplicationTypes_All, _
    productUriString, _
    serverCapabilities, _ 
    lastCounterResetTime, _
    nextRecordId, _
    applicationDescriptionArray
If Err.Number <> 0 Then
    WScript.Echo "*** Failure: " & Err.Source & ": " & Err.Description
    WScript.Quit
End If
On Error Goto 0

' For each application returned by the query, find its registrations in the GDS.
Dim ApplicationDescription
For Each ApplicationDescription In applicationDescriptionArray
    WScript.Echo
    WScript.Echo "Application URI string: " & ApplicationDescription.ApplicationUriString

    On Error Resume Next
    Dim applicationRecordArray: applicationRecordArray = GlobalDiscoveryClient.FindApplications( _
        gdsEndpointDescriptor, _
        ApplicationDescription.ApplicationUriString)
    If Err.Number <> 0 Then
        WScript.Echo "  *** Failure: " & Err.Source & ": " & Err.Description
    Else
        Dim ApplicationRecord
        For Each ApplicationRecord In applicationRecordArray
            ' Display results
            WScript.Echo "  Application ID: " & ApplicationRecord.ApplicationId
        Next
    End If
    On Error Goto 0
Next


' Example output:
'
'Application URI string: urn:sampleserver
'  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=09ecaa08-6ec6-462c-a214-1e66a3099107
'
'Application URI string: urn:alarmconditionserver
'  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=783e1e9a-8036-43b6-928f-97488c460266
'
'Application URI string: urn:PC:MultiTargetUADocExamples:5.54.1026.1:neutral:null
'  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=9e700ea5-55a6-4c3c-ba9f-b91c890dc519
'
'Application URI string: urn:PC:UADocExamples:5.56.0.16:neutral:null
'  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=e182e28c-086b-4fc7-82c7-70ca7cda3033
'
'Application URI string: urn:PC:cscript:5.812.10240.16384
'  Application ID: nsu=http://opcfoundation.org/UA/GDS/applications/ ;ns=2;g=aec94459-f513-4979-8619-8383555fca61

Python

# Shows how to find all registrations in the GDS.
#
# Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
# OPC client and subscriber examples in Python on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-Python .
# Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
# a commercial license in order to use Online Forums, and we reply to every post.
# The QuickOPC package is needed. Install it using "pip install opclabs_quickopc".
import opclabs_quickopc

# Import .NET namespaces.
from System import *
from OpcLabs.EasyOpc.UA import *
from OpcLabs.EasyOpc.UA.Discovery import *
from OpcLabs.EasyOpc.UA.Extensions import *
from OpcLabs.EasyOpc.UA.Gds import *
from OpcLabs.EasyOpc.UA.OperationModel import *


# Define which GDS we will work with.
gdsEndpointDescriptor = UAEndpointDescriptor('opc.tcp://opcua.demo-this.com:58810/GlobalDiscoveryServer')
gdsEndpointDescriptor = UAEndpointDescriptorExtension.WithUserNameIdentity(gdsEndpointDescriptor,
                                                                           'appadmin', 'demo')

# Instantiate the global discovery client object.
globalDiscoveryClient = EasyUAGlobalDiscoveryClient()

# Find all (client or server) applications registered in the GDS.
try:
    _, _, _, applicationDescriptionArray = globalDiscoveryClient.QueryApplications(
        gdsEndpointDescriptor,
        0,  # startingRecordId
        0,  # maximumRecordsToReturn
        '', # applicationName
        '', # applicationUriString
        UAApplicationTypes.All, # applicationTypes
        '', # productUriString
        Array.Empty[String](),  # serverCapabilities
        DateTime(), # out lastCounterResetTime
        0,  # out nextRecordId
        Array.Empty[UAApplicationDescription]()) # out applications
except UAException as uaException:
    print('*** Failure: ' + uaException.GetBaseException().Message)
    exit()

# For each application returned by the query, find its registrations in the GDS.
for applicationDescription in applicationDescriptionArray:
    print()
    print('Application URI string: ', applicationDescription.ApplicationUriString, sep='')

    try:
        applicationRecordArray = globalDiscoveryClient.FindApplications(
            gdsEndpointDescriptor,
            applicationDescription.ApplicationUriString)
    except UAException as uaException:
        print('  *** Failure: ' + uaException.GetBaseException().Message)
        continue

    # Display results.
    for applicationRecord in applicationRecordArray:
        print('  Application ID: ', applicationRecord.ApplicationId, sep='')

print()
print('Finished.')

X.509 certificate token

The X.509 certificate token authenticates the user by its user certificate (challenge-response process is used underneath). This token is represented by the X509CertificateTokenInfo Class object in the UserIdentity.X509CertificateTokenInfo Property.

In order to specify the user certificate that will be used, you fill in a certificate query, contained in the CertificateQuery Property of the X509CertificateTokenInfo object. The certificate query specify where the certificate comes from, and which certificate should be selected. The source of the certificate is given by the source type (SourceType Property), and depending on its value, further properties in the certificate query object need to be filled in.

The X.509 certificate token info is considered configured when the source type is not None.

In .NET languages, instead of manipulating the object properties, you can take an OPC UA endpoint descriptor, and create a new endpoint descriptor from it, with user identity provided by the X.509 certificate token. To do so, use the WithX509CertificateIdentity Method

Dynamic User Identity Change

OPC UA has a feature that allows the clients to change the identity of the user on an existing connection (OPC UA session), without having to close the session and re-open it. OPC Data Client supports this feature as well. For more information, see Dynamic User Identity Change.

Note that this feature is only available if the OPC UA server supports it.

See Also

Examples - OPC UA GDS and CM

Reference

Concepts

Examples - Client OPC UA User Authentication