7802 Views // 0 Comments // Not Rated

The HybridProvider - Retrieving Passwords

I originally wrote the Hybrid Provider using all of the default settings for its underlying SQL and AD providers. The basic paradigm I tackled was that "internal" users would be authenticating against AD; their accounts are essentially (except for profile information) read only to SharePoint.

SQL users, however, are external, and we completely control the full life cycle of their accounts: everything from creation to password management to deletion. The admin tools I created to manage SQL users in SharePoint provide the functionality to administrators to work with these accounts. So what I do is put all SQL users in one SharePoint group, and audience target the web parts hosting admin user controls or ASP.NET login controls to them.

For our AD users, however, like I said, everything should be read only. The only place we should consider manipulating AD data for these accounts should be on the internal network, not through code. To this end, the AD membership provider throws an exception if you so much as try to retrieve a password with the intention of changing it; it is *very* read only.

(I did a post about retrieving AD information in SharePoint. Again, this is all querying; any attempt to even attempt to update data will blow up. It is useful in applications to get group membership information, etc. from AD; I just feel that programmatically manipulating your forest is generally a bad idea.)

So in this discussion, I will be focusing on SQL users, and therefore the SQL membership provider.

The SqlMembershipProvider does support password retrieval and resetting, unlike AD. There are three different ways that it encrypts passwords in the database: clear text, encrypted, and hashed; the later being the default. And as I stated, the first cut of the HybridProvider used the hashed by default, which meant that my passwords were one-way encrypted, and forever stored as long goofy strings.

So the first time a user forgot their password, we couldn't get it back! So, since we support this use case, something had to be done. The other two password formats the SQL membership provider uses are straight-forward: clear text stores passwords exactly how they are typed, which is obviously a security no-no. Encrypted is therefore the answer, since it is stored securely in the database, but can be decrypted back to normal text.

The first thing I tried was to change my web.config settings to force the HybridProvider's SQL provider to use the encrypted password format. Here's what to do:

Code Listing 1

  1. <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" enablePasswordRetrieval="true" enablePasswordReset="true" passwordFormat="Encrypted" />

Once you save the web.config, the provider will automatically be using the new password format, and any methods you call on the base MembershipProvider class that deal with password retrieval or resetting should now work...

Well, as I found, it "kind of" works. What I mean is that, after making this change, all accounts created heretofore will be using the new format, and using password retrieval from the out-of-the-box ASP.NET 2.0 login controls will work. However, existing accounts will continue to use the hashed password format!

So here's the rule: the password format is not technically a provider-level setting; it really is on an account-by-account basis. The passwordFormat attribute merely sets the default that each account primordially uses. If you use the encrypted format from the inception of your HybridProvider, you'll be fine. But if you started out hashed, (like I did) you're only half way there.

But never fear: there's always a way. You can write some SQL to update accounts to use the new password format directly in the database. Now I know this is a massive hack...but the only way to get rid of a hashed password is to blow it away. Here's what to do:

Code Listing 2

  1. UPDATE aspnet_membership
  2. SET
  3. password = 'copy-and-pasted password here',
  4. passwordformat = 2,
  5. passwordsalt = 'copy-and-pasted salt here',
  6. IsLockedOut = 0,
  7. FailedPasswordAttemptCount = 0
  8. WHERE userid = (SELECT userid FROM aspnet_users WHERE username = 'user name here')

What we have now is an account using the encrypted password format ("2" in the enumeration). And, we know the password, so we can finish the job by Emailing the user their new credentials and asking them to change their password.

(If you haven't guessed yet, my SharePoint portal has SmartParts hosting ASP.NET login controls all over the place. They do a great job of wrapping all of the the basic user maintenance functionality I need for my SQL accounts, and all I have to do is set the MembershipProvider property to "HybridProvider" and it's all wired up!)

You can either run this query on a user-by-user basis when they need to reset their password, or write an app (or even just a cursor) to do this for all accounts and send an Email to each user. Again, I know it's hacky, but I haven't had success dealing with hashed passwords any other way. Have fun!

No Tags

No Files

No Thoughts

Your Thoughts?

You need to login with Twitter to share a Thought on this post.