ASP.NET Membership Provider - Reset Password Features - Email Confirmation and Password Change

pelican_george picture pelican_george · Jun 28, 2010 · Viewed 9.6k times · Source

Does anyone have a solution (sample code) for the following features:

  • Create a randomGuid/Cryptographically strong random number
  • Send a unique URL containing the random number to the user's email address
  • When confirmed, the user is asked to change password

My provider is currently parametrized this way:

enablePasswordRetrieval="false" 
enablePasswordReset="true" 
requiresQuestionAndAnswer="false" 
applicationName="/" 
requiresUniqueEmail="true" 
passwordFormat="Hashed" 
maxInvalidPasswordAttempts="5" 
minRequiredPasswordLength="5" 
minRequiredNonalphanumericCharacters="0" 
passwordAttemptWindow="10" 
passwordStrengthRegularExpression="" 
name="AspNetSqlMembershipProvider"

The security issues with this type of procedure have been discussed here before.

Answer

Ryk picture Ryk · Jul 2, 2010

Rui, I was in a similar boat a few moths ago, and not having had a great deal of exposure to the membership provider before, I struggled to get it implemented properly, because I thought you have to implement and use the whole thing as is, or nothing at all.

Well I soon discovered that you can only use parts of it, not the entire object. For example in my application I have my own user object and my own password and login modules, but I needed to get forms authentication working. So I basically just use the Membership provider for that part. I am not overriding any methods or anything, just use it for FormsAuthentication, then I use my own repositories to changing passwords, verifying username passwords etc.

Here is a snippet from my login code in MVC.

   var authTicket =  new FormsAuthenticationTicket(1, user.UniqueName, DateTime.UtcNow, DateTime.UtcNow.AddHours(2), rememberMe, cookieValues);
    var encryptedTicket = FormsAuthentication.Encrypt(authTicket);
    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) { Expires = DateTime.UtcNow.AddDays(14) };

    httpResponseBase.Cookies.Add(authCookie);

This enables me to use the FormsAuthentication.SignOut(); methods and other methods the membership provider makes available.

On your question re Create a randomGuid/Cryptographically strong random number, here is a good method you can use, I found it a while ago, and I am sorry but can't remember where on the web

/// <summary>
/// A simple class which can be used to generate "unguessable" verifier values.
/// </summary>
public class UnguessableGenerator
{
    const string AllowableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/^()";
    const string SimpleAllowableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    /// <summary>
    /// Generates an unguessable string sequence of a certain length
    /// </summary>
    /// <param name="length">The length.</param>
    /// <param name="useSimpleCharacterSet">if set to <c>true</c> [use simple character set].</param>
    /// <returns></returns>
    public static string GenerateUnguessable(int length, bool useSimpleCharacterSet = false, string additionalCharacters = "")
    {
        var random = new Random();

        var chars = new char[length];

        var charsToUse = useSimpleCharacterSet ? SimpleAllowableCharacters : AllowableCharacters;
        charsToUse = string.Format("{0}{1}", charsToUse, additionalCharacters);

        var allowableLength = charsToUse.Length;

        for (var i = 0; i < length; i++)
        {
            chars[i] = charsToUse[random.Next(allowableLength)];
        }

        return new string(chars);
    }




    /// <summary>
    /// Generates an ungessable string, defaults the length to what google uses (24 characters)
    /// </summary>
    /// <returns></returns>
    public static string GenerateUnguessable()
    {
        return GenerateUnguessable(24);
    }
}

For Send a unique URL containing the random number to the user's email address, what you need is in your User table or any table a way to store a random id and then use that in a crafted url in an email that the user can use to verify.

This will require you to create a Url that will accept that type of Url, and then you have to extract the parts you need form the querystring and update your user object.

For When confirmed, the user is asked to change password, by default have a bit flag in your table that forces a user to change his password, this will come in handy later too if you need to force a user to change password. So then your login code looks to see if this bit flag is on, and if so, it will force a password change first. Then once the user has changed his password, you will turn this flag off.

Hope this helps.