Client Authentication

Working with client sessions

After opening secure channel, the client needs to create a session. The client will receive a set of endpoints and a list of supported user identity token polices in response.

local session, err = client:createSession("test_session", 3600000)
if err ~= nil then
  error("Creating session failed: "..err)
end

local tokenPolicy
for _, endpoint in ipairs(session.ServerEndpoints) do
  for _, policy in ipairs(endpoint.UserIdentityTokens) do
    if policy.TokenType == ua.Types.UserTokenType.UserName and
      (policy.SecurityPolicyUri == nil or policy.SecurityPolicyUri == ua.Types.SecurityPolicy.None)
    then
      tokenPolicy = policy
      goto found
    end
  end
end

::found::
if not tokenPolicy then
  error("cannot find endpoint with username token.")
end

local userName = "admin"
local password = "12345"
local resp, err = client:activateSession(tokenPolicy.PolicyId, userName, password)
if err ~= nil then
  error("Activating session failed: "..err)
end

Full source

Each token policy contains several fields. The following fields are common to all policy types:

  • PolicyId

    a string identifier assigned to the policy by the server. This identifier is used by the server to understand the parameters of the user token sent by the client.

  • TokenType

    The token type: Anonymous, UserName, Certificate, Issued Token.

  • SecurityPolicyUri

    The URI of the security policy used to secure the token.

Additional fields are specific to each token type and will be covered in the following sections.

Anonymous

An anonymous token basically means the absence of authentication, which means anyone can connect to and work with the server. This type of authentication is usually enabled for testing purposes only.

You can pass the identifier of the anonymous policy, which is taken from the response of CreateSession. No additional parameters are required for the anonymous token.

-- Create session with name "test_session" and with life time
-- 1 hour (3600000 ms)
local session, err = client:createSession("test_session", 3600000)
local tokenPolicy
for _, endpoint in ipairs(session.ServerEndpoints) do
  for _, policy in ipairs(endpoint.UserIdentityTokens) do
    if  policy.TokenType == ua.Types.UserTokenType.Anonymous
    then
      tokenPolicy = policy
      goto found
    end
  end
end

::found::
if not tokenPolicy then
  error("cannot find endpoint with anonymous token.")
end
-- Passing id of anonymous token policy.
local resp, err = client:activateSession(tokenPolicy.PolicyId)

Full source

User Name

Depending on the server configuration, a password can be sent encrypted or unencrypted, which depends on the token policy configuration on the server.

The Username token policy has the following parameters:

  • PolicyId

    Id of a policy on assigned by server

  • TokenType

    Token type has value ua.Types.UserTokenPolicy.UserName

  • SecurityPolicyUri

    URI of the security policy that defines what encryption should be applied for the password. If this field is absent or points to the security policy None, then no encryption is applied to the password. If the secure channel doesn’t apply encryption for all packets, then the password will be sent as is.

The following example demonstrates how to authenticate with a server using a username and password:

local session, err = client:createSession("test_session", 3600000)
if err ~= nil then
  error("Creating session failed: "..err)
end

local tokenPolicy
for _, endpoint in ipairs(session.ServerEndpoints) do
  for _, policy in ipairs(endpoint.UserIdentityTokens) do
    if policy.TokenType == ua.Types.UserTokenType.UserName and
      (policy.SecurityPolicyUri == nil or policy.SecurityPolicyUri == ua.Types.SecurityPolicy.None)
    then
      tokenPolicy = policy
      goto found
    end
  end
end

::found::
if not tokenPolicy then
  error("cannot find endpoint with username token.")
end

local userName = "admin"
local password = "12345"
local resp, err = client:activateSession(tokenPolicy.PolicyId, userName, password)
if err ~= nil then
  error("Activating session failed: "..err)
end

Full source

x509 certificate

To authenticate with a certificate, you need to find the ID of the policy with the type ua.Types.UserTokenPolicy.Certificate.

This policy has the following parameters:

  • PolicyId

    Id of a policy on assigned by server

  • TokenType

    Token type has value ua.Types.UserTokenPolicy.Certificate

  • SecurityPolicyUri

    URI of the security policy that defines if client will also send a signature. The signature is calculated with using corresponding private key of a user and proves owning of certificate by current client. If this field is absent or points to the security policy ‘None’, then the certificate is sent alone without a signature.

When activating a session with certificate-based authentication, the ActivateSession call should include the following parameters:

  • TolicyId

    ID of token policy Certificate

  • TokenType

    Token type has value ua.Types.UserTokenPolicy.Certificate

  • Certificate

    Certificate of the user. Is sent to cerver.

  • PrivateKey

    Private key of corresponding user’s certificate. Used to calculate signature.

Activate session accepts certificate and provate key in pem, der format or can be passed path to files.

The following example show how to authenticate using a certificate:

  error("Opening secure channel failed: "..err)
end

local session, err = client:createSession("test_session", 3600000)
local tokenPolicy
for _, endpoint in ipairs(session.ServerEndpoints) do
  -- Select certificate token policy with security policy Basic128Rsa15
  for _, policy in ipairs(endpoint.UserIdentityTokens) do
    if  policy.TokenType == ua.Types.UserTokenType.Certificate then
      tokenPolicy = policy
      goto found
    end
  end
end

::found::
if not tokenPolicy then
  error("cannot find endpoint with certificate token policy.")
end

local certificate = mako.cfgdir.."/../certs/client.pem"
local privateKey = mako.cfgdir.."/../certs/client.key"
local resp, err = client:activateSession(
  tokenPolicy.PolicyId, certificate, privateKey)

Full source

JWT, OAuth2, Azure

OPC UA allows authentication using third-party tokens, which are also known as Issued Tokens.

To authenticate with such tokens, you need to search for the policy that uses the token type ua.Types.UserTokenPolicy.IssuedToken. This type of token is commonly used for all third-party tokens. To distinguish between token formats, the policy includes an additional parameter called IssuedTokenType.

The IssuedToken policy has the following parameters:

  • PolicyId

    ID of token policy Certificate

  • TokenType

    Token type has value ua.Types.UserTokenPolicy.Certificate

  • TssuedTokenType

    This is a string that specifies the type of issued token and can have one of the following values:

    - ua.Types.IssuedTokenType.JWT
    - ua.Types.IssuedTokenType.OAuth2
    - ua.Types.IssuedTokenType.Azure
    
  • IssuerEndpointUrl

    Url of the server that issued the token

  • SecurityPolicyUri

    URI of the security policy that defines what encryption should be applied for token. If this field is absent of points to security policy None tnen no encryption for password is applied. if secure channel doesn’t apply encryption for all packets then password will be sent as is.

The following example shows how to authenticate with issued token:

  error("Opening secure channel failed: "..err)
end

local session, err = client:createSession("test_session", 3600000)
local tokenPolicy
for _, endpoint in ipairs(session.ServerEndpoints) do
  for _, policy in ipairs(endpoint.UserIdentityTokens) do
    -- Select JWT token policy. There also Azure, OAuth2 and OPCUA.
    if  policy.TokenType == ua.Types.UserTokenType.IssuedToken and
        policy.IssuedTokenType == ua.Types.IssuedTokenType.JWT
    then
      tokenPolicy = policy
      goto found
    end
  end
end

::found::
if not tokenPolicy then
  error("cannot find endpoint with certificate token policy.")
end

local jwtHeader = { alg="HS256", typ = "JWT"}
local jwtPayload = {
  sub = "1234567890",
  name = "John Doe",
  iat = 1516239022
}
local token = require"jwt".scomp("my-secret", jwtPayload, jwtHeader)
local resp, err = client:activateSession(tokenPolicy.PolicyId, token)

Full source