So if you've been programming with the latest in .NET (excluding 2010 and 4.0) then you are probably getting familiar with your new friend WCF. I love WCF because it clears away a lot of the noise associated with old asmx services. You can write agnostic services, that happen to be web enabled, instead of writing web services that you might have to port to local implementations later. If you are not a WCF believer, then I suggest you drink the Kool-aid already; you won't look back. WCF's true power comes from security and configuration, and it's ability to play nice with ASP.NET, so the other day I decided that I wanted to use the membership provider and role provider in ASP.NET.
MonstersGotMy.Net.Web.Security.zip (8.06 mb)
I created a web application and proceeded to add the necessary providers, just like you probably have. It worked great, I used the SQL memberhsip provider and it was butter. Then it struck me, one of my constraints is that I cannot access the database directly. CRAP! Ok so I'm back to square one. So I did some digging on MSDN and found that .NET 3.5 has a new feature under the System.Web.ApplicationServices namespace. Great! So I began setting it up, and got it to work authenticating user logins. Now it was time to write an admin tool, so that this application could manage itself. But hold on a minute, what is this? Damn it again! Look at this :

it seems the client provider doesn't implement any of the necessary methods needed to manage the users. It only implement the methods needed to validate a single user. This was a total bummer, although I understand the reasoning behind it. Microsoft didn't want to expose those methods in a web service; that might be a security risk. But WCF is all about security, so I decided to throw caution to the wind. WCF is like a pancake stack of security, you pile on as much as you can stomach.
So I got out the trusty old reflector tool, to see what was ahead of me.... crap.... more hash tables. If you have dealt with asmx, wcf, or asp.net you realize that the HashTable class is the kiss of death for any serialization process. Although the class is marked serializable, it is not. Curse you Microsoft developer 1344, writer of the hash table.

So I trecked ahead. What I needed to do, was to create three things: a provider, a service, and a client. The way I constructuted the service, is that the service uses a local MembershipProvider. In my case I decided to use the SqlMembershipProvider. Then I needed to expose that provider through a service. So I created an Interface based on the MembershipProvider class but had to change somethings around, mainly the nasty HashTables/Collections; you can see what I did below. Finally I created the clients and included them in the same assembly, so that the clients would be reuseable. After a bit of coding I finally got it! It works, check it out. The below image is on the client side, calling to a WCF service. You probably want to download the assembly right away and try it out. I did the same for the RoleProvider. Sadly, the ProfileProvider was too deep in the mess that is HashTable; some of the objects in the ProfileSettings class also decides to pass around the provider. In the end, the profile provider had to be left behind.
[ServiceContract]
public interface IWcfMembershipProviderService
{
[OperationContract]
bool ChangePassword(string username, string oldPassword, string newPassword);
[OperationContract]
bool ChangePasswordQuestionAndAnswer(string username, string password,
string newPasswordQuestion,string newPasswordAnswer);
[OperationContract]
MembershipUser CreateUser(string username, string password,
string email, string passwordQuestion,
string passwordAnswer, bool isApproved,
object providerUserKey,
out MembershipCreateStatus status);
[OperationContract]
bool DeleteUser(string username, bool deleteAllRelatedData);
[OperationContract]
IList FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize,
out int totalRecords);
[OperationContract]
IList FindUsersByName(string usernameToMatch, int pageIndex, int pageSize,
out int totalRecords);
[OperationContract]
IList GetAllUsers(int pageIndex, int pageSize, out int totalRecords);
[OperationContract]
int GetNumberOfUsersOnline();
[OperationContract]
string GetPassword(string username, string answer);
[OperationContract]
MembershipUser GetUserWithKey(object providerUserKey, bool userIsOnline);
[OperationContract]
MembershipUser GetUser(string username, bool userIsOnline);
[OperationContract]
string GetUserNameByEmail(string email);
[OperationContract]
string ResetPassword(string username, string answer);
[OperationContract]
bool UnlockUser(string userName);
[OperationContract]
void UpdateUser(MembershipUser user);
[OperationContract]
bool ValidateUser(string username, string password);
[OperationContract]
string GetApplicationName();
[OperationContract]
void SetApplicationName(string applicationName);
[OperationContract]
bool GetEnablePasswordReset();
[OperationContract]
bool GetEnablePasswordRetrieval();
[OperationContract]
int GetMaxInvalidPasswordAttempts();
[OperationContract]
int GetMinRequiredNonAlphanumericCharacters();
[OperationContract]
int GetMinRequiredPasswordLength();
[OperationContract]
int GetPasswordAttemptWindow();
[OperationContract]
MembershipPasswordFormat GetPasswordFormat();
[OperationContract]
string GetPasswordStrengthRegularExpression();
[OperationContract]
bool GetRequiresQuestionAndAnswer();
[OperationContract]
bool GetRequiresUniqueEmail();
}
And here is the client configuration, this is the basic configuration, but you can set the application name, from the client, how cool is that? Well I lied, that's not all the configuration. There is still the WCF configurations for the service and client, and also the MembershipProvider and RoleProvider on the host. I swear, that's it, no more configuration... unless your a glutton for punishment.
So If you are ready to download this project, here is the project.The project is large, because of the aspnetdb.
MonstersGotMy.Net.Web.Security.zip (8.06 mb)