diff --git a/Libraries/Opc.Ua.Client/Session/Session.cs b/Libraries/Opc.Ua.Client/Session/Session.cs
index ef0f6b5185..ebc51c8ade 100644
--- a/Libraries/Opc.Ua.Client/Session/Session.cs
+++ b/Libraries/Opc.Ua.Client/Session/Session.cs
@@ -1283,6 +1283,8 @@ namespace Opc.Ua.Client
previousServerNonce,
m_endpoint.Description.SecurityMode);
+ m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
+
// sign data with user token.
SignatureData userTokenSignature = identityToken.Sign(
dataToSign,
@@ -2324,7 +2326,7 @@ namespace Opc.Ua.Client
UserTokenPolicy identityPolicy = endpoint.FindUserTokenPolicy(
m_identity.TokenType,
m_identity.IssuedTokenType,
- endpoint.SecurityPolicyUri);
+ m_userTokenSecurityPolicyUri ?? endpoint.SecurityPolicyUri);
if (identityPolicy == null)
{
@@ -2337,18 +2339,21 @@ namespace Opc.Ua.Client
}
// select the security policy for the user token.
- string? tokenSecurityPolicyUri = identityPolicy.SecurityPolicyUri;
- if (string.IsNullOrEmpty(tokenSecurityPolicyUri))
+ if (m_userTokenSecurityPolicyUri == null)
{
- tokenSecurityPolicyUri = endpoint.SecurityPolicyUri;
+ string? tokenSecurityPolicyUri = identityPolicy.SecurityPolicyUri;
+ if (string.IsNullOrEmpty(tokenSecurityPolicyUri))
+ {
+ tokenSecurityPolicyUri = endpoint.SecurityPolicyUri;
+ }
+ m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
}
- m_userTokenSecurityPolicyUri = tokenSecurityPolicyUri;
// validate server nonce and security parameters for user identity.
ValidateServerNonce(
m_identity,
m_serverNonce,
- tokenSecurityPolicyUri,
+ m_userTokenSecurityPolicyUri,
m_previousServerNonce,
m_endpoint.Description.SecurityMode);
@@ -2357,7 +2362,7 @@ namespace Opc.Ua.Client
identityToken.UpdatePolicy(identityPolicy);
SignatureData userTokenSignature = identityToken.Sign(
dataToSign,
- tokenSecurityPolicyUri);
+ m_userTokenSecurityPolicyUri);
// encrypt token.
identityToken.Encrypt(
diff --git a/Libraries/Opc.Ua.Server/Diagnostics/AuditEvents.cs b/Libraries/Opc.Ua.Server/Diagnostics/AuditEvents.cs
index 3cafe0132c..6d0148d113 100644
--- a/Libraries/Opc.Ua.Server/Diagnostics/AuditEvents.cs
+++ b/Libraries/Opc.Ua.Server/Diagnostics/AuditEvents.cs
@@ -1072,7 +1072,7 @@ namespace Opc.Ua.Server
e.SetChildValue(
systemContext,
BrowseNames.UserIdentityToken,
- Utils.Clone(session?.IdentityToken.Token),
+ Utils.Clone(session?.IdentityToken?.Token),
false);
server.ReportAuditEvent(systemContext, e);
diff --git a/Tests/Opc.Ua.Client.Tests/ClientTest.cs b/Tests/Opc.Ua.Client.Tests/ClientTest.cs
index 815c63a970..1a197a8ea2 100644
--- a/Tests/Opc.Ua.Client.Tests/ClientTest.cs
+++ b/Tests/Opc.Ua.Client.Tests/ClientTest.cs
@@ -956,6 +956,65 @@ namespace Opc.Ua.Client.Tests
Utils.SilentDispose(session2);
}
+ ///
+ /// Open a session on a channel using a usertokenpolicy from another endpoint, then reconnect (activate)
+ ///
+ [Test]
+ [Order(260)]
+ [TestCase(SecurityPolicies.Basic256Sha256, SecurityPolicies.Basic128Rsa15)]
+ public async Task ReconnectSession_ReuseUsertokenPolicyAsync(
+ string securityPolicy, string userTokenPolicy)
+ {
+
+ UserIdentity userIdentity = new UserIdentity("user1", "password"u8);
+
+ // the first channel determines the endpoint
+ ConfiguredEndpoint endpoint = await ClientFixture
+ .GetEndpointAsync(ServerUrl, securityPolicy, Endpoints)
+ .ConfigureAwait(false);
+ if (endpoint == null)
+ {
+ NUnit.Framework.Assert.Ignore(
+ $"No endpoint found for {securityPolicy}");
+ }
+ endpoint = new ConfiguredEndpoint(
+ null,
+ (EndpointDescription)endpoint.Description.MemberwiseClone(),
+ endpoint.Configuration);
+ endpoint.Description.SecurityMode = MessageSecurityMode.Sign;
+ ConfiguredEndpoint tokenPolicyEndpoint = await ClientFixture
+ .GetEndpointAsync(ServerUrl, userTokenPolicy, Endpoints)
+ .ConfigureAwait(false);
+ Assert.NotNull(tokenPolicyEndpoint);
+
+ UserTokenPolicy identityPolicy = endpoint.Description.FindUserTokenPolicy(
+ userIdentity.TokenType,
+ userIdentity.IssuedTokenType,
+ tokenPolicyEndpoint.Description.SecurityPolicyUri);
+ if (identityPolicy == null)
+ {
+ NUnit.Framework.Assert.Ignore(
+ $"No UserTokenPolicy found for {userIdentity.TokenType}" +
+ $" / {userIdentity.IssuedTokenType}");
+ }
+ userIdentity.PolicyId = identityPolicy.PolicyId;
+
+ // the active channel
+ ISession session1 = await ClientFixture.ConnectAsync(endpoint, userIdentity)
+ .ConfigureAwait(false);
+ Assert.NotNull(session1);
+ try
+ {
+ await session1.ReconnectAsync(null, null).ConfigureAwait(false);
+ }
+ finally
+ {
+ session1.DeleteSubscriptionsOnClose = true;
+ await session1.CloseAsync(1000).ConfigureAwait(false);
+ Utils.SilentDispose(session1);
+ }
+ }
+
///
/// Open a session on a channel, then recreate using the session as a template,
/// verify the renewUserIdentityHandler is brought to the new session and called