Monday, February 23, 2015

You say you want users of your WCF web service to play nicely with the custom means your application has for managing who is logged in, huh?

Well, these links have the solution for you:

 
 

You'll need a class that inherits from UserNamePasswordValidator like so in the project which holds the web service:

using System;
using System.IdentityModel.Selectors;
namespace WebApplication
{
   public class CustomyValidator : UserNamePasswordValidator
   {
      public override void Validate(string userName, string password)
      {
         if (null == userName || null == password)
         {
            throw new ArgumentNullException();
         }
         if (!(userName == "foo" && password == "bar"))
         {
            throw new Exception("Yikes!");
         }
      }
   }
}

 
 

Alright, this upfront sanity check will provide the chance to throw an exception and block access if the username and password look strange. This means that both the application holding the WCF web service and the application consuming it will need to hold references to System.IdentityModel at the applicable projects. The hardest part of getting all this to work for me was messaging Web.config into shape. This bit of Web.config...

<system.serviceModel>
   <behaviors>
      <serviceBehaviors>
         <behavior name="">
            <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="false" />
         </behavior>
      </serviceBehaviors>
   </behaviors>
   <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
         multipleSiteBindingsEnabled="true" />
</system.serviceModel>

 
 

...eventually got rewritten as:

<system.serviceModel>
   <services>
      <service name="WebApplication.CornyWebService"
            behaviorConfiguration="CornyBehavior">
         <host>
            <baseAddresses>
               <add baseAddress ="http://www.experiment.com/CornyWebService.svc" />
            </baseAddresses>
         </host>
         <endpoint address="username" binding="wsHttpBinding"
               bindingConfiguration="Binding"
               contract="WebApplication.ICornyWebService" />
      </service>
   </services>
   <bindings>
      <wsHttpBinding>
         <binding name="Binding">
            <security mode="Message">
               <message clientCredentialType="UserName" />
            </security>
         </binding>
      </wsHttpBinding>
   </bindings>
   <behaviors>
      <serviceBehaviors>
         <behavior name="CornyBehavior">
            <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults ="true"/>
            <serviceCredentials>
               <userNameAuthentication userNamePasswordValidationMode="Custom"
                     customUserNamePasswordValidatorType="WebApplication
                     .CustomyValidator, WebApplication" />
               <serviceCertificate findValue="localhost" storeLocation="LocalMachine"
                     storeName="My" x509FindType="FindBySubjectName" />
            </serviceCredentials>
         </behavior>
      </serviceBehaviors>
   </behaviors>
</system.serviceModel>

 
 

At the other app which actually tried to authenticate against the web service I had code like so:

CornyWebServiceClient cornyWebServiceClient = new CornyWebServiceClient();
cornyWebServiceClient.ClientCredentials.UserName.UserName = "foo";
cornyWebServiceClient.ClientCredentials.UserName.Password = "bar";
cornyWebServiceClient.ClientCredentials.ServiceCertificate.Authentication
      .CertificateValidationMode = System.ServiceModel.Security
      .X509CertificateValidationMode.PeerOrChainTrust;
ViewBag.PassOrFail = cornyWebServiceClient.FishForValidation();

 
 

...which gave me this error:

The X.509 certificate CN=localhost is not in the trusted people store. The X.509 certificate CN=localhost chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

 
 

I got around this problem by using this setting instead for the CertificateValidationMode:

System.ServiceModel.Security.X509CertificateValidationMode.None;

No comments:

Post a Comment