I'd like to use UserNamePasswordValidator with wsDualHttpBinding but here is my problem:
If I use:
<security mode="Message" >
<message clientCredentialType="UserName" />
</security>
I get this exception:
Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was 'localhost' but the remote endpoint provided DNS claim 'Theater'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'Theater' as the Identity property of EndpointAddress when creating channel proxy.
If I change the DNS to Theater then it times out. If i don't use the security mode the UserNamePasswordValidator doesn't get called. I read that if I would use wsHttpBinding I could set the security to TransportWithMessageCredential and that would work but I need the dual. Here is the config:
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding maxReceivedMessageSize="268435456" maxBufferPoolSize="268435456">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="268435456" maxBytesPerRead="4096" maxNameTableCharCount="2147483647" />
<security mode="Message" >
<message clientCredentialType="UserName" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<services>
<service name="WcfService1.Service1" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsDualHttpBinding" contract="WcfService1.IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService1.OperatorValidator, WcfService1"/>
<serviceCertificate findValue="Theater" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
Validator:
namespace WcfService1
{
public class OperatorValidator : UserNamePasswordValidator
{
public override void Validate(String userName, String password)
{
if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))
throw new SecurityTokenException("Username and password cannot be empty.");
try
{
using (Theater4Entities entities = new Theater4Entities())
{
Byte[] passwordBytes;
using (SHA1CryptoServiceProvider provider = new SHA1CryptoServiceProvider())
{
passwordBytes = provider.ComputeHash(Encoding.UTF8.GetBytes(password));
}
Operator currentOperator = entities.Operator.FirstOrDefault(op => op.UserName == userName);
if (currentOperator == null || !passwordBytes.SequenceEqual(currentOperator.UserPassword))
throw new SecurityTokenException("Username or password does not match.");
}
}
catch (SecurityTokenException)
{
throw;
}
catch
{
throw new SecurityTokenException("Unexpected error occured.");
}
}
}
}
And here is where I catch the exception:
public WAF1ClientViewModel()
{
_callbackService = new RentalServiceCallback();
InstanceContext context = new InstanceContext(_callbackService);
_client = new Service1Client(context);
}
.
.
.
private void LoginExecuted(PasswordBox passwordBox)
{
try
{
_client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
_client.ClientCredentials.UserName.UserName = UserName;
_client.ClientCredentials.UserName.Password = passwordBox.Password;
_client.Login(UserName);
_isLoggedIn = true;
OnLoginSuccess();
}
catch
{
OnLoginFailed();
}
}
I would be open to any alternative method.