This solution illustrates a simple username/password token with custom authentication through WSE 3.0 with C#.  There is one project for the web service and one for the sample client.

Code was written March 2007 by Carl Nelson, carl@carlnelson.com.

This code was published on http://blog.carlnelson.com/.

Code may be freely distributed with this file intact and attributed Carl Nelson if republished.  You may do whatever the heck you like with the code other than that; conversely, the code is supplied as-is with no guarantees of functionality.

If you find this code helpful enough, nothing says "thank you" like something off my Amazon wishlist:

http://www.amazon.com/gp/registry/wishlist/1C98VJOA5OB4M/ref=wl_web/

Hint, hint. ;-)

*************
Program Notes
*************

I've been developing a web service that authorizes based on the Web Services Enhancements (WSE) 3.0 specification.  It's been a fairly cumbersome exercise, mainly due to poor documentation on the what and why of the process, so I'm writing down the process that I went through to create it.

-Install Microsoft's WSE 3.0 and add references to Microsoft.Web.Services3 to your projects.  (I've included the using directives in the full code, but not in the listings below, so download the samples and look through them if you're interested in which namespaces to include.)

-Develop your custom username/password validation routine by deriving it from UsernameTokenManager and overriding the AuthenticateToken method.  The generic one that I did was this:
public class SampleUsernameTokenManager : UsernameTokenManager
{
  protected override string AuthenticateToken(UsernameToken token)
  {
    bool Valid = (("MyUsername" == token.Username) && ("MyPassword" == token.Password));
    if (Valid)
    {
      return token.Password;
    }
    else
    {
      throw new UnauthorizedAccessException("User validation failed");
    }
  }
}
You would, of course, use whatever routine you needed to determine the validity of the UsernameToken.  The major consideration here is that you have to return the password if the credentials are valid.

-Change your web.config file by doing the following: 
+Make sure that configuration/configSections contains the following:
 <section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
+Make sure that configuration/webServices contains the following:
 <soapExtensionImporterTypes>
  <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</soapExtensionImporterTypes>
<soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
+Make sure that configuration/microsoft.web.services3 contains the following:
 <security>
  <securityTokenManager>
    <add type="SampleWSEService.SampleUsernameTokenManager, SampleWSEService" namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken" />
  </securityTokenManager>
</security>
<policy fileName="wse3policyCache.config"/>
The securityTokenManager element is what binds the specific security method to the class that determines the validity of the token.  The type attribute needs to refer to the fully-qualified name of the class that derives from UsernameTokenManager in the first part and have the name of the DLL containing it in the second.  The important thing to note here is that the namespace and localName attributes must be exactly as shown above!

-Change your wse3policyCache.config file to look like this (note that this is the minimum policy file for my example; you may want to combine other authorization methods in this file as well):<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
  <extensions>
    <extension name="usernameOverTransportSecurity" type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </extensions>
    <policy name="SamplePolicy">
    <authorization>
      <allow user="*"/>
    </authorization>
    <usernameOverTransportSecurity />
    <requireActionHeader />
  </policy>
</policies>
Some important considerations here are: 
+Remember the name that you give the policy; you'll use that to bind your web service to this policy.
+The authorization section includes a default deny.  We add an allow for all users ("*") rather than for all authenticated users ("?") because we're sending text usernames; if we were doing Windows-based authentication we'd restrict it to authenticated users only.
+Alternately, you could list all valid usernames in the authorization section like this:
 <allow user="Fred"/>
 <allow user="Dolores"/>
+Basically the authorization section allows listed users to be processed by the appropriate security token manager; it doesn't imply that they are allowed access--only that they are allowed to reach the access mechanism you've defined.

-Bind the policy to your class that derives from WebService with the policy attribute right before the class declaration, like this:
[Policy("SamplePolicy")]
public class Sample : System.Web.Services.WebService

-To enable the client to use this, add a web reference to your web service and use the following code:
// Make sure to connect to the Wse proxy for the service
MySampleWSEService.SampleWse MyWebService = new MySampleWSEService.SampleWse();// Create the username token
string Username = "MyUsername";
string Password = "MyPassword";
UsernameToken MyToken = new UsernameToken(Username, Password, PasswordOption.SendPlainText);
MyWebService.SetClientCredential(MyToken);// Create the policy and specify username over transport authentication
Policy MyPolicy = new Policy();
MyPolicy.Assertions.Add(new UsernameOverTransportAssertion());
// Apply the policy to the exchange
MyWebService.SetPolicy(MyPolicy); 
string Response = MyWebService.HelloWorld();