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