For future self-reference (mostly)…
WCF in Framework 3.5 allows you to use a custom user name / password based authentication mechanism to authenticate individual service requests. This is nice.
This functionality (as with seemingly everything else in WCF) involves a tiny amount of code and a ton of configuration. This is less nice.
Note: These steps just allow you to do pretty much the most primitive authentication. Everything sent is still in the clear. So for the love of all things pure (and some that are not) don’t use this in production without with some kind of encryption.
That said, here’s how to set it up…
First, create a class deriving from System.IdentityModel.Selectors.UserNamePasswordValidator. Note, you’ll have to add a reference to System.IdentityModel.dll not System.IdentityModel.Selectors.dll. The SecurityTokenValidationException is in System.IdentityModel.Tokens.
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using NDL.Data;
namespace NDL.DataService
{
public class BasicAuthenticationValidator : UserNamePasswordValidator
{
private static UserDataManager mUserDataManager;
/// <summary>
/// Performs static initialization for the BasicAuthenticationValidator.
/// </summary>
static BasicAuthenticationValidator()
{
mUserDataManager = XmlDataManager.Instance.UserDataManager;
}
/// <summary>
/// Performs validation using the given user name and password.
/// </summary>
/// <param name="userName">The user name supplied.</param>
/// <param name="password">The password supplied.</param>
public override void Validate(string userName, string password)
{
//Let the lower layer handle the authentication.
if (mUserDataManager.AuthenticateUser(userName, password) == false)
{
throw new SecurityTokenValidationException("Authentication failed.");
}
}
}
}
With the easy part out of the way, you now have to set up the service’s configuration to start using it.
In the binding section on both the server and client, modify the security configuration to enable TransportCredentialsOnly security mode.
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
Next, in the behavior section, set the validation mode and specify the type we created to handle the authentication.
<serviceCredentials>
<userNameAuthentication
customUserNamePasswordValidatorType="NDL.DataService.BasicAuthenticationValidator,
NDL.DataService"
userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
Finally, you can set the credentials on the UserName property of the service client’s ClientCredentials property.
//Set up the service proxy.
DataServiceClient client = new DataServiceClient();
client.ClientCredentials.UserName.UserName = "user";
client.ClientCredentials.UserName.Password = "supersecret";
//Execute some service calls....
client.RetrieveCustomerList();
That’s it! I guess it’s really not that bad. (Ask me again in a few months when I’ve forgotten it all again…)
References: