Search code examples
authenticationworkflow-foundation-4wcf-security

How to set up WorkflowService authentication?


I just need to secure my WF services. Can't find any resources on this. How to do it?

Already tried:

class Program
{
    static void Main(string[] args)
    {
        using (WorkflowServiceHost host = new WorkflowServiceHost(new Workflow1(), new Uri("http://localhost/Test")))
        {
            host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
            host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new Test();

            host.Open();
            Console.Write("ready");
            Console.ReadLine();
        }
    }
}
public class Test : UserNamePasswordValidator
{
    public Test()
    {
        Console.Write("hit");
    }

    public override void Validate(string userName, string password)
    {
        Console.Write("never hit");
    }
}

And a config

<bindings>
  <wsHttpBinding>
    <binding>
      <security mode="Message">
        <message clientCredentialType="UserName" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

  <serviceBehaviors>
    <behavior>
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="false"/>
      <!--<serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="myAssembly.Test, myAssembly" />
      </serviceCredentials>-->
    </behavior>
  </serviceBehaviors>
  • Can't create a fixed name endpoint because they are dynamically created

UPDATE - I tried the configuration bellow and worked, but I want a more granular way to set what binding each service use

<protocolMapping>
  <add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>

Solution

  • A little hackish, but works. Overrided WorkflowServiceHost in order to grab unknown contract names and added service endpoints for each one.

        const string DEFAULT_WORKFLOW_SERVICE_BINDING_NAME = "WorkflowDefaultBinding";
    
        static void Main(string[] args)
        {            
            MyWorkflowServiceHost host = new MyWorkflowServiceHost(new CountingWorkflow2(), new Uri(hostBaseAddress));
            foreach (var contractName in host.ImplementedContractsNames)
            {
                // now I'm able to choose which binding to use depending on a condition
                var binding = new WSHttpBinding(DEFAULT_WORKFLOW_SERVICE_BINDING_NAME);
    
                host.AddServiceEndpoint(contractName, binding, string.Empty);
            }
        }
    

    And MyWorkflowServiceHost

        public class MyWorkflowServiceHost : WorkflowServiceHost
        {
            public MyWorkflowServiceHost(Activity activity, params Uri[] baseAddresses)
                : base(activity, baseAddresses)
            {
    
            }
    
            private IDictionary<string, System.ServiceModel.Description.ContractDescription> _implementedContracts;
            public IEnumerable<string> ImplementedContractsNames
            {
                get
                {
                    foreach (var contract in _implementedContracts)
                        yield return contract.Key;
                }
            }
    
            protected override System.ServiceModel.Description.ServiceDescription CreateDescription(out System.Collections.Generic.IDictionary<string, System.ServiceModel.Description.ContractDescription> implementedContracts)
            {
                System.ServiceModel.Description.ServiceDescription description = base.CreateDescription(out implementedContracts);
    
                _implementedContracts = implementedContracts;
    
                return description;
            }
        }
    

    Adding a unamed WSHttpBinding and the following section on service model should work too, but for default configuration

    <protocolMapping>
      <add scheme="http" binding="wsHttpBinding"/>
    </protocolMapping>