Thursday, April 30, 2009

Introducing WCF ClearUsernameBinding

@YaronNaveh




Update
: ClearUsernameBinding is now hosted on GitHub. This post contains the updated usage instructions.


Using cleartext username/password is usually not recommended. However it is sometimes required (like with F5's BIG-IP). WCF does not natively allow us to use such scenario. For this reason I have written ClearUsernameBinding - a WCF binding that enables to send cleartext username/password over HTTP.

Full source code is available in google code github.

So without any further preparations let's see how to use ClearUsernameBinding.

Step 1: Download latest release
Download it here or go to google code github.
Then extract the zip to some folder, let's say C:\program files\ (the ClearUsernameBinding subfolder will be created when extracting the zip).

Step 2 (optional) - Run the sample project
It can be useful to run the sample application.

Run the server:

C:\program files\ClearUsernameBinding\TestService\bin\Release\
TestService.exe




And now the client:


C:\program files\ClearUsernameBinding\TestClient\bin\Release\
TestClient.exe




And if everything went smoothly you have just seen ClearUsernameBinding in first action!

Step 3 (optional) - Investigate the sample project source code
The best way to learn a new (and very simple in this case) technology is by looking at existing projects. Just open with VS 2008 the solution file:


C:\program files\ClearUsernameBinding\ClearUsernameBinding.sln


And look at the source of the projects TestClient and TestService. These two projects are just normal WCF projects configured to use ClearUsernameBinding. In other words, making a WCF client/service use ClearUsernameBinding is just a matter of changing web.config and does not require coding. We will see in the next steps how to do it from scratch.

I'll probably have a separate post on the binding implementation itself. It is pretty straight forward and the handling of security is as I learned from Nicholas Allen's blog.

Step 4 - Creating your own service
For this step just create any normal WCF web site or a self hosted service.

Step 5 - Configure the service to use ClearUsernameBinding
Add your project a dll reference to


C:\Program Files\ClearUsernameBinding\ClearUserPassBinding\bin\
Release\ClearUsernameBinding.dll


Then open web.config and register the ClearUsernameBinding under the system.ServiceModel section:



<extensions>
  <bindingExtensions>
   <add name="clearUsernameBinding" type="WebServices20.BindingExtenions
.ClearUsernameCollectionElement, ClearUsernameBinding" />
  </bindingExtensions>
</extensions>

<bindings>
  <clearUsernameBinding>
   <binding name="myClearUsernameBinding"
messageVersion="Soap12">
   </binding>
  </clearUsernameBinding>
</bindings>


Finally configure your endpoint to use ClearUsernameBinging and its configuration:


<endpoint binding="clearUsernameBinding" bindingConfiguration="myClearUsernameBinding"
contract="WebServices20.SampleService.IEchoService" />


An example of the complete web.config is inside the full project binary&source in


C:\Program Files\ClearUsernameBinding\TestService\bin\Release\
TestService.exe.config


Step 6 (optional) - Configure the message version
If you need to use a specific message version configure it in the "messageVersion" attribute in the above configuration. Valid values are: Soap11WSAddressing10, Soap12WSAddressing10, Soap11WSAddressingAugust2004, Soap12WSAddressingAugust2004, Soap11, Soap12, None, Default.

Example:


<binding name="myClearUsernameBinding" messageVersion="Soap12">
</binding>


Step 7 - Configure the username authentication
This one needs to be done in any username/password authenticated service and not just one that uses ClearUsernameBinding. By default your server will authenticate the users against your active directory domain. If you want to do your own custom authentication you need to create a new class library project with a class that implements System.IdentityModel.Selectors.UserNamePasswordValidator

The class can look like this:



public class MyUserNameValidator : UserNamePasswordValidator
{
  public override void Validate(string userName, string password)
  {
   if (userName != "yaron")
    throw new SecurityTokenException("Unknown Username or Password");
  }
}


Don't forget to add dll reference to System.IdentityModel and System.IdentityModel.Selectors or the project will not compile. Then add this project as a project reference to your service project/website and configure the latter to use this custom authenticator:


<behaviors>
  <serviceBehaviors>
   <behavior name="SampleServiceBehaviour">
...
    <serviceCredentials>
    <userNameAuthentication     userNamePasswordValidationMode="Custom"
    customUserNamePasswordValidatorType=
"WebServices20.Validators.MyUserNameValidator, MyUserNameValidator" />
    </serviceCredentials>
   </behavior>
  </serviceBehaviors>
</behaviors>
...
<service behaviorConfiguration="SampleServiceBehaviour" name="WebServices20.SampleService.EchoService">
...


Again the full sample is available for download.


Step 8 - Run the service
Yes, the service is now ready to be activated, so run it when you are ready (run it directly from VS, just press F5).


Step 9 -Build a client
A service is worth nothing if there are no clients to consume it.
Create a new console application.
Right click the "References" node in the solution explorer and choose "Add service reference". Specify the WSDL of the server. If you are running the server from the given sample then the wsdl is in http://localhost:8087/SampleService/?WSDL. If you used your own server just run it and get the wsdl.

Now add some client code that uses the proxy to call the service. Don't forget to specify your username/password. For example:



ServiceReference1.EchoServiceClient client = new TestClient.ServiceReference1.EchoServiceClient();
client.ClientCredentials.UserName.UserName = "yaron";
client.ClientCredentials.UserName.Password = "1234";
client.EchoString("hello");



Step 10 - Configure the client
Configuring the client is as simple as configuring the service.
Here is the full client app.config:



<system.serviceModel>
  <client>
   <endpoint address="http://localhost.:8087/SampleService/" binding="clearUsernameBinding"
   bindingConfiguration="myClearUsernameBinding"   contract="ServiceReference1.IEchoService"
   name="ClearUsernameBinding_IEchoService" />
  </client>

  <extensions>
   <bindingExtensions>
    <add name="clearUsernameBinding"    type="WebServices20.BindingExtenions
.ClearUsernameCollectionElement   , ClearUsernameBinding" />
   </bindingExtensions>
  </extensions>

  <bindings>
   <clearUsernameBinding>
    <binding name="myClearUsernameBinding"
messageVersion="Soap12">

    </binding>
   </clearUsernameBinding>
  </bindings>

</system.serviceModel>



Step 11 - Done, Done, Done!
That's all. You can now run your client and see how WCF can be used to access a service with a cleartext username/password. Use a tool like fiddler to verify that indeed a clear username is sent (I've shorten some low-level stuff from bellow message):


<Envelope>
  <Header>
   <Security>
...
    <UsernameToken>
     <Username>yaron</Username>
     <Password>1234</Password>
    </UsernameToken>
   </Security>
  </Header>
  <Body>
   <EchoString xmlns="http://tempuri.org/">
    <s>hello</s>
   </EchoString>
  </Body>
</Envelope>


Conclusion
Sending username/password on the clear is not available out of the box with WCF (for reasons mentioned above). If such a scenario is required then ClearUsernameBinding needs to be used.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

242 comments:

«Oldest   ‹Older   201 – 242 of 242   Newer›   Newest»
Yaron Naveh (MVP) said...

sunny

does this also happen in the demo project? typically this means the bindingExtensions element is not configured correctly.

Craig and Jessie's Pepper Patch said...

Yaron,

Thanks for your help with this code and forum. I am also having trouble with the maximum message size quota. I have tried to change the code in the project, but I can not get it to compile, could you make a dll with the maximum message size set to max?

Yaron Naveh (MVP) said...

Craig

Can you get the original project to compile (e.g. w/o any cahnges)?

Unknown said...

Hi guy, This is very useful topic. I tried to implement this concept to my project. It work very well in local. But when I publish this service to internet, then I just modified the address in client config. But I got this error "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.". Please help.

Unknown said...

Hi guy, This is very grateful topic. I implemented this concept to my project. I work very well on local. But when I published It to internet I got this error "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state."

I adjusted address in client config base internet server address. My internet server does not require ssl.

Please help. Thank you in advance.

Yaron Naveh (MVP) said...

Tung Nguyen

you should turn on WCF tracing and logging on the server and client to get more information.

Unknown said...

Hi,

This is error message I found when I do tracing

“ The security timestamp is stale because its expiration time ('2012-04-05T02:11:13.174Z') is in the past. Current time is '2012-04-05T18:13:25.364Z' and allowed clock skew is '00:05:00'”

Could you tell me how to use SecurityBindingElement.LocalClientSettings.maxClockSkew with ClearUserName Binding to fix this error please.

Thanks

Ronan said...

Hi Yaron,

First of all, thanks for this post, it's been really helpful so far!

I have got the ClearUsernameBinding up and running in my project, but I was wondering how to retrieve the username in the service method itself?

I tried setting the Thread.CurrentPrincipal to a GenericPrincipal in the Validate method of the UserNamePasswordValidator, but when I get to my service method, the CurrentPrincipal is reverted to a WindowsPrincipal..

Yaron Naveh (MVP) said...

Hi Ronan

try

OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name

Anonymous said...

Hi Yaron;

My application showed the following error:
Failed to add a service. Service metadata may not be accessible. Make sure your service is running and exposing metadata.

My app.config:

Yaron Naveh (MVP) said...

anonymous - please send me the details in email

Anonymous said...

Hi. I am able to use this custom binding. But, when I attempt to call a java web service using WS Security, I get this error. System.ServiceModel.FaultException: MustUnderstand headers:[{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood

What does this mean?

Yaron Naveh (MVP) said...

one option is that java does not require security at all, this is why it does not understand the security header. check that you are sending to the right endpoint.

the other options is that java needs security but does not like these headers. verify that by asking the server owner for a working sample. if this is the case you need to use a custom message encoder in wcf to remove the header.

Martin O said...

Hi Yaron,

Great work btw.

I used your api in my .Net 4.0 project. The VS prompted me that I couldn't debug my service because somehow it was disabled. I didn't disable anything. when i returned to the normal certificate based authentication, the error was gone. i'm wondering what could be the cause for this and if it has a solution?

thank you so much

Yaron Naveh (MVP) said...

Hi Martin

Not sure I uderstand. If normal certificate works for you, why do you need CUB? The .net 4 thing may be b/c CUB was originally compiled for 3 but of you open the project in VS it should prompt you to migrate.

Unknown said...

I'm receiving next error:
An error occurred creating the configuration section handler for system.serviceModel/bindings: No parameterless constructor defined for this object.
It thrown when I'm creating ServiceHost. Config exactly the same as in the example.

Yaron Naveh (MVP) said...

@Mikhail - do you also get it on my sample app?

Unknown said...

Hi Yaron. Your project "Clear Username Binding" works well for me. But the problem is that I want wcf server to be host in WCF Service Host. (Like if you create in VS any service libray from template it will be runned in WCF Service Host). I've done next thing. I 've created new project (service library) and simply copy your service and contract to this new project. Then I copied app.config "system.serviceModel" section and added referenced to ClearUserNameBinding project and MyUserNameValidator. That's all
Next I run the client application. All was ok. WCF Service Host had been started and client application ended as I expected. But the problem is that now if I put a breakpoint somewhere in the service implementation (let it be somewhere inside of the EchoString function) and run again I always get message in VS IDE "Unable to automatically debug WCFServiceLibrary". I really don't know what to do with this error and how to get rid of it, because I need the debugging to be available for me.
I can provide source project if my explanation of the problem wasn't very clear
P.S. This error is appearing only when I use ClearUserNameBinding. If I use for example wsHttpBinding it works perfect.

Thanks in advance.

Yaron Naveh (MVP) said...

Hi Alex

This error does not seem related to CUB. You say that it only happens in CUB so maybe CUB is compiled to a different platform or alike from your project. Since you have the source of CUB you can compile it to the same platform or even add its source code to your project and compile together.
In addition CUB ships with a sample self-hosted server and client so you can try to see if they are debuggable.

freon said...

Hi Yaron!

TY for what you've done, really useful. However there is one problem.

I use dual-endpoint configuration on both of my client and server:
1) Endpoint with basicHttpBinding and TransportWithMessageCredential. This is secured https endpoint for production-like environment testing.
2) Endpoint with CUB and TransportWithMessageCredential. This is unsecured http endpoint for unit-testing (my unit test server does not have ssl enabled).

The service is hosted on IIS. The problem is when I debug client which uses CUB (no matter, real client or unit test or something else) I get error which was mentioned above: "Unable to automatically debug 'ServiceName'." on first proxy method invocation. As far as I switch the same code to use basicHttpBinding all goes well, VS finds symbols and debugs service code as well as client. Debug symbols are enabled as well as debug compilation in service in web.config. So the problem is truly CUB.

I also found question on SO related to this issue: http://stackoverflow.com/questions/15241901/clear-username-binding-unable-to-automaticalli-debug-wcf-service
Also, very similar question here: http://stackoverflow.com/questions/315210/unable-to-debug-wcf-service-message

I struggled solving this problem but the only solution is to use basicHttpBinding for debugging which is not always suitable for me.
Maybe you will have an idea on how to fix it?

Yaron Naveh (MVP) said...

Hi Freon

Check out my answer here:

http://stackoverflow.com/questions/15241901/clear-username-binding-unable-to-automaticalli-debug-wcf-service/16173694#16173694

You could try to attache to iis/aspnet process, or try a custom binding (I didn't check the latter).

Unknown said...

Its not working in IIS.
Its always giving error as
"An error occurred when verifying security for the message."

Can you provide any sample using IIS.

Yaron Naveh (MVP) said...

Hi Amin

Please turn on WCF log and trace and mail them to me.

Amin said...

Hi Yaron,

Its working, I have a service reference in a separate project and host is different, so every-time I was updating the reference, it was updating the web.config of project not the host , i was modified on the project not the host and that was the issue, thanks a lot for your great work.

now I am facing different issue , I want to validate the request against actual users in the Database and dont want the database call in every request ,can you suggest me any method for this.

Yaron Naveh (MVP) said...

you need to use some binding that supports sessions but this would typically require secure conversation which requires a server certificate which I guess you try to avoid from the first place since you use CUB. If you are ok with it then use a custom binding with secrity mode of "SecureConversation" and bootstrap security mode of "autoSecuredHttpTransport" (from the CUB dll). But again if you can use certificate you are probably better with otehr options than CUB.

Another option will be to try a session binding without certificate but I'm not sure if it is possible. Maybe there are ways to use HTTP cookies or reliable messaging.

Isaac said...

I've been using this ClearUsernameBinding approach for years, but is there a way for enforce a server-side timeout? I have the client giving up after 60 seconds, but the WCF service will continue to chug away consuming resources. I would like it to give up after X seconds.

Is there any way to do this other than starting up a thread that fires a TimeoutException inside the service method?

Yaron Naveh (MVP) said...

This question is not specific to CUB but general to WCF. I'm not sure about it. Maybe one of the settings under security/localServiceSettings (in a custom binding) would help, for example

negotiationTimeout="00:02:00"

inactivityTimeout

if you find how to do it w/o CUB then it is easy to do it with.

Nalaka526 said...

Worked nicely with SOAP UI Client.. Thank you very much...

Amin said...

Hi Yaron,

This binding is giving me below error.
Unable to automatically debug 'WcfService1'. The remote procedure could not be debugged. This usually indicates that debugging has not been enabled on the server. See help for more information.

This is specifically happening with nunit project.

can you please tell what could be the reason.

I have created the sample project fro you, if you want i can send you that or anybody who is interested.

let me know the email id or how can i attached the sample project here.

Regards
Amin

Yaron Naveh (MVP) said...

Hi Amin

Is the service deployed on iss / VS web server / self hosted? In general in order to debug you need to attach to the relevant process after it is created, for example process "wcf service host".

Amin said...

I have posted reply to above question but it did not appear here .

again
I am using VS development server.
tried IIS also.

Yaron Naveh (MVP) said...

Hi Amin

put a breakpoint on the first line of the client (before it calls the service) and at that point attach to process "wcf service host". Then breakpoints should work. If this still does not work can you put a brekapoint on wcf services that do not use CUB?

Unknown said...

HI Thanks For Useful Post.
I Have One Problem .In This Sample You Add Reference To ClearUserPassBinding.dll Directly In Test Client Project .Is there Any Way To By Pass This Because My Clients Work With Other Technologies Such PHP Or Java

Yaron Naveh (MVP) said...

Hi Ali

If your clients do not use .Net then they do not need to add ClearUsernameBinding.dll to their project. They need to configure their framework to send the clear username according to what their framework supports.

Unknown said...

Hi Yaron,
First of all thanks for such a great library.

I have one question though; can I use CUB without the config file. Just using code.
I have tried the following:
var binding = new clearUsernameBinding();
EndpointAddress ea = new EndpointAddress("service_url");
ServiceClient client = new ServiceClient();
client.ClientCredentials.UserName.UserName = "someuser";
client.ClientCredentials.UserName.Password = "somepassword";
client.doOperation();

But got the exception:
Envelope Version 'EnvelopeNone (http://schemas.microsoft.com/ws/2005/05/envelope/none)' does not support adding Message Headers.

Thanks,
Shehab.

Yaron Naveh (MVP) said...

Hi Shehab

You did not add the binding to the proxy:

new ServiceClient(binding , ea)

if you still get the same exception see if you get it also with other binding (e.g. basichttp) - maybe it is not related to CUB.

Unknown said...

Hi Yaron,
Sorry it was just a typing mistake. I have added the binding and address to client.
If I try with BasicHttpBinding I get "Content Type text/xml; charset=utf-8 was not supported by service ...".

I have also stopped the service that I connect to when trying CUB with config and BasicHttpBinding I get "There was no endpoint listening at ...". When I try with CUB with code I get "Envelope Version 'EnvelopeNone (http://schemas.microsoft.com/ws/2005/05/envelope/none)' does not support adding Message Headers."

So it seems that when constructing CUB by code something wrong happens, any idea?

Best regards,
Shehab.

Yaron Naveh (MVP) said...

Hi Shehab

Can you create a sample WCF project that reproduce this bug and open as a defect in Github?

Yaron Naveh (MVP) said...

Hi

After you create the binding add this code:

binding.SetMessageVersion(System.ServiceModel.Channels.MessageVersion.Soap11);

dan said...

Hi Yaron,

Please ignore my previous comment - I have fixed the issue. The problem was that I was specifying the wrong endpoint address! (That's what happen when you code line is too long and it goes off the side of the screen!)

Thanks for the code by the way - I'm on an internal network without certificates, so this is perfect.

Henry said...

Hi There,
I like to adopt your clear usename lib in my wcf app. Thanks for your contributions. Not sure you still answer questions with this lib-related. I downloaded your source codes and compiled (I have VS2010) and the output from running is fine. When I replaced
client.ClientCredentials.UserName.UserName = "yaron" in testclient program with any other string value like :
"fake_user"; It throws a System.ServiceModel.FaultException. How can I catch the SecurityTokenException("Unknown Username or Password") exception and message?

Thanks,
Henry

Yaron Naveh (MVP) said...

Hi Henry

The sample server is hard coded to accept only this specific username (search for "yaron" in the code). If you build your own server you will have your own authentication approach.

«Oldest ‹Older   201 – 242 of 242   Newer› Newest»