XML membership provider for ASP.NET 2.0

Oct 1, 2006

ASP.NET ships with a SqlMembershipProvider and a ActiveDirectoryMembershipProvider that makes user authentication and authorization very easy to implement, but for some reason those are the only membership providers provided natively by ASP.NET 2.0. So, what do you do when you don't want or can't use SQL Server or Active Directory for memberships? You have to build a custom membership provider that suits your needs.

That’s exactly what I faced when I wanted an XML membership provider for a small web project. The only one I could find on the web was a very simple read-only xml provider from MSDN. I then changed it to be writable as well, so you can dynamically add new users among other things. I also encrypted the passwords so no one is able to make sense out of them when looking at the XML file.

It’s plug n’ play, it works and it makes user authentication ridiculously easy. Download the code at the bottom and dump the XmlMembershipProvider.cs class into the App_Code folder and the users.xml into the App_Data folder. Then write this in the web.config.

<membership defaultProvider="XmlMembershipProvider">

  <providers>

    <add name="XmlMembershipProvider" type="XmlMembershipProvider" description="XML membership provider" xmlFileName="~/App_Data/users.xml"/>

  </providers>
</membership>

Now you have a membership provider that enables you to make use of the collection of built in authentication controls. You can also interact directly with the provider without using the built in controls. Here's an example of how to create a new user:

MembershipCreateStatus status;

Membership.Provider.CreateUser("admin", "adminpw", "admin@domain.com", string.Empty, string.Empty, true, "admin", out status);

Even though you can access the provider programmatically, nothing beats the simplicity of the built in controls. To start using the XmlMembershipProvider, drag a CreateUserWizard to your webform and let the magic begin.

Download

XmlMembershipProvider.zip (3,45 KB)

* $4.95/month BlogEngine.net Hosting – Click Here!

Comments (20) -

 Jacob
Jacob
10/2/2006 1:31:23 PM #

Awesome -- I had made a few simple mods to the MSDN read-only version myself, and had it on the back burner to build a writable version.  Haven't tried yours yet, but based on the quality other code you've published, you've saved me a lot of time and trouble.  Thanks!  I'll report back when I set it up.

Mads Kristensen
Mads Kristensen
10/2/2006 1:51:17 PM #

Jacob, it hasn't been testet in a live environment yet, so if you find any errors or things you would like to change, please let me know and I'll fix it.

Afshin
Afshin Iran
4/4/2009 1:10:27 AM #

I am developing an ASP.net website using VB.net 2008. So I changed the code to VB. It works on my PC but when I deploy the site on the web, the pages work with membership return HTTP Internal Server Error 500.

 Dee
Dee
10/3/2006 10:54:46 PM #


VB version:


Dim status As MembershipCreateStatus

Membership.Provider.CreateUser("admin", "adminpw", "admin@domain.com", String.Empty, String.Empty, True, "admin", status)

NinjaCross
NinjaCross
10/4/2006 2:21:37 PM #

Thanks for this usefull contribute Smile
Just a question: is there a reason for the presence of those unsupported overrides in the lower part of the file ? Didn't you implement them beacuse you didn't need for their functionalities or there are some deeper motivations (i.e. an incongruence with the provider model pattern as it was originaly thought by MS) ?

Mads Kristensen
Mads Kristensen
10/4/2006 5:09:44 PM #

There is no real reason behind not implementing some of the methods other than I have no use for them. Password retrievel is impossible because it is on-way encrypted, so those methods are not implemented on purpose.

NinjaCross
NinjaCross
10/4/2006 7:28:35 PM #

Thanks for the explanation.
This is a so interesting approach to the Membership subsystem and I'm planning to do something similar in a future project of mine.
The only really important thing that would miss at this point is an implementation for an Xml Roles Provider.

mik
mik
11/28/2006 2:11:29 PM #

Mads, I'm trying your provider but I get this error:

Object reference not set to an instance of an object.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Line 255:    _Users.Add(username, user);

I've set up a default.aspx using only the CreateUserWizard control. Upon 'create user' the error pops up! - although the user is written to the xml file.

Needs the XmlMembershipProvider to be explicit instantiated or what did I miss?

 J. Michael Palermo IV
J. Michael Palermo IV
12/19/2006 5:40:53 AM #

mik - remove line 255.  It is not needed.

 Phil
Phil
1/16/2007 9:50:18 AM #

mik,
I get the same error when the users.xml file is empty - I hard coded adding the first user just to add myself and then commented it out. When I try to start up the site I get an exception but as soon as I then uncomment the line the user exists and I don't get the problem anymore.

What I do get, however, is an inconsistency in the hash I can never login because I seem to get a different hash everytime - will investigate further this evening. Has anyone else had a similar problem? The whole thing seemed to work last night and now it just doesn't - how confusing....

 Phil
Phil
1/16/2007 3:39:29 PM #

Hmm sure I saw different hashes coming out when I debugged last night but not now - I have literally run the hash 500000000 times and got the same result so I must have been imagining it. I had started adding a few more of the MembershipUser properties turns out what I was doing there was killing things.

Just wondering why you made the reading/writing XML the "hard" way using DOM and looking at each XMLNode manually instead of simply using XmlSerializer?

 J. Gravelle
J. Gravelle
2/22/2007 4:17:27 PM #

MK,

Glad I saw Phil's last two posts... or I'd have thought I was going nuts(-er).

Had the same issue, where the hashing mechanism was spitting out different results sporadically.  SHA256Managed was the culprit.

The fix, while not as secure as your current code (read: "good enough for my little Intranet-based gizmo") is shown here:

  private static string Encrypt(string plainMessage)
  {
    byte[] data = Encoding.UTF8.GetBytes(plainMessage);
    //using (HashAlgorithm sha = new SHA256Managed())
    using (HashAlgorithm sha = new SHA1CryptoServiceProvider())
    {
      //byte[] encryptedBytes = sha.TransformFinalBlock(data, 0, data.Length);
        byte[] encryptedBytes = sha.ComputeHash(data);
      return Convert.ToBase64String(sha.Hash);
    }
  }

I also had to modify ReadMembershipDataStore() since the "_Users" dictionary stays cached forever, ignoring updates:

      //if (_Users == null)
        if (true)

There's really no overhead hit.  I suppose if I had a zagillion users there might be... but that'd be time to switch offa XML anyway.


Hope this helps somebody.  Thanks man...


-jjg

 Matt Lyons
Matt Lyons
2/25/2007 9:28:07 AM #

Hi guys,

I was hoping you could help me with a problem I was having.

I have been trying to incorporate your XML membership provider into a website of mine. However, because I already have a VB class, and VWD doesn't like classes of different languages, I have had to compile it into a DLL using csc.exe. I have added a reference to it, and it now shows up in the Object Browser. I put the code above into my web.config file, but when I use the .NET web administration tool, it throws a Null Reference Exception ("Object reference not set to an instance of an object").

Any ideas? Any help would be greatly appreciated.

Cheers,
Matt

palako
palako
7/26/2007 10:20:14 PM #

Hi Mads.

First, of course, thanks for this mod of the msdn readonly version, it was useful.

It's almost a year ago when you posted this, so I hope you get to reach this comment.

I've spent some hours fighting with your code because of several inconsistencies, regarding to the hashes, and I've ended up fixing two parts of your code:

First, the updateUser method is called from the user validation to update the last login date and from the users manager to update the password/comment, but, depending of the caller, the password is already chipered, so you don't have to encrypt again. I removed the Encrypt call from this line to work well, because on every login the hash was beeing updated calculated over the old hash, not the password itself, thus, never working.

The other bug was in the changePassword method, where you just update the xml file but not the _Users cache, so what I did was updating the user local var and call UpdateUser, which, indeed, updates the xml file.

I've just coded this, I'm trying it for some days to see if it works ok, and If you want I can send you the modified code. Just drop me an email as I don't think I'll check here these comments for your answer, it's an old topic.

Hope to be as helpfull as you were to me,

Regards,

jonah
jonah Sweden
8/24/2007 6:26:44 AM #

I guess the code

if (node["UserName"].InnerText.Equals(username, StringComparison.OrdinalIgnoreCase)
        || node["Password"].InnerText.Equals(Encrypt(oldPassword), StringComparison.OrdinalIgnoreCase))

is supposed to be

if (node["UserName"].InnerText.Equals(username, StringComparison.OrdinalIgnoreCase)
        && node["Password"].InnerText.Equals(Encrypt(oldPassword), StringComparison.OrdinalIgnoreCase))

so both the UserName AND the Password validates... or am I wrong?

- jonah

Chris Blankenship
Chris Blankenship United States
12/11/2007 2:56:38 AM #

Hey Mads, I went looking for an XML provider for my website and ran across a provider in Codeplex called XMLProvider 1.3 (http://www.codeplex.com/aspnetxmlproviders).  I wonder how this compares to your provider.  It seems to be working great but thought I would get your opinion.

Moe Howard
Moe Howard United States
3/8/2008 4:12:11 PM #

Excellent work... bro.

engwar
engwar United States
7/4/2008 8:52:23 PM #

I'm attempting to use your XML provider for my website and am running into two issues.

1. When testing I occasionally am told my password is wrong even when I am sure I am entering the correct value. It's as if the Encrypt method isn't working consistently.

2. I want to create a page where I can administer my users. I try to bind the MembershipUserCollection to a GridView using the GetAllUsers method. It can't find the _XmlFileName as if Initialize() hasn't run. I'm using the following code to attempt to bind that collection to my GridView.

XmlMembershipProvider myProvider = new XmlMembershipProvider();
int x;
GridView1.DataSource = myProvider.GetAllUsers(0, 0, out x);
GridView1.DataBind();

Any help is appreciated.

Reena
Reena India
1/9/2009 4:36:07 AM #

Hi Mads

Thanks - your XmlMemebershipProvider Saved me a lot of time

Few Things I would like to share with you

1. Doubt :For encrypting the password why have you not used the FormsAuthentication.HashPasswordForStoringInConfigFile() or the membership provider method EncryptPassword instead of writing code yourself. please let me know if there is any specific reason for doing the same.
I have used FormsAuthentication.HashPasswordForStoringInConfigFile

2. I get an Object reference not set to an instance of an object.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
At the end of function Validate User.

3. In the ChangePassword function I have changed the Or operater to And operated as I thought that both coditions where required to be true to allow ChangePassword to happen

Reena
Reena India
1/11/2009 6:01:30 AM #

Hi Mads

Forgot to mention that I have The xmlMembership provider implemented in VB.net

In the function ValidateUser, just as in C# In VB I have

Dim user As Membershipuser
If (_MyUsers.TryGetValue(username.ToLower(), user)) Then
......
EndIf

Here It gives me error: Object null reference for "user" since it is passed by Ref it is expected to be instatiated before being used.

I have tried instantiating it and using as
Dim user As New MembershipUser(String.Empty, String.Empty, username, String.Empty, String.Empty, String.Empty, False, False, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now)
and using which I don't think is right but the error still persits

Any help on this will be highly appreciatiated

Thanks in advance

Pingbacks and trackbacks (1)+

Comments are closed

About the author

Mads Kristensen

Mads Kristensen
Program Manager at the Microsoft Web Platform team and founder of BlogEngine.NET.

More...

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.