All Articles

ASP.NET Identity 1.0: where the hell is the recover password feature?

Answer: it's not available! Surprised!? Yes, I am very surprised!

 

For many years we've used the ASP.NET Membership (or the SimpleMembership included in Webpages). Here are the features included out-of-the box in the membership system:

  • Authentication
  • User Registration
  • Roles Management
  • Password Management
  • Profile Management

 

With .Net 4.5.1 we have a new identity management system called ASP.NET Identity

. It's normal to compare the two systems, since they have the same main goal, which is to provide an identity management system.

 

I don't want to go with the details of this new system, and the reasons that took Microsoft to write it. You can check a great introduction here.

 

If you decide to use this new system (as I did in my current project) to provide a basic user management system in you application, allowing a user to have an usual username/password credentials, you will be very surprised (and maybe disapointed) by the fact that the password recover feature it's not included out-of-the-box. Of course we can implement it by hand, but the truth is that the ancient Membership system has included this feature, so it seems obvious to think and expect that this new one would also include it. Wrong assumptions. So, if you check the UserManager class (the main class to manage identity features), you won't find any method to generate a reset password token to be used by the application user to be allowed to reset his password.

 

After some research, it seems that the next version of the ASP.NET Identity, version 1.1, will include the recover password feature.

Since October 2013, Microsoft decided to make available the nightly builds of the ASP.NET Identity system. You can get this nightly builds using NuGet packages available on MyGet. Check the steps to follow in Visual Studio to configure the MyGet feed to pull in the nightly packages for ASP.NET Identity

image

 

What I will be showing next is based on the packages with version 1.1.0-alpha1-131108 that I grabbed in beginning of November, which is a pre-release version, and maybe the things have change since then.

 

Checking again the UserManager class, we now have some methods/properties/interfaces related to the password recover feature

public interface ITokenProvider
{
    string Generate(UserToken token);
    UserToken Validate(string token);
}

public class UserManager<TUser, TKey> : IDisposable
    where TUser : global::Microsoft.AspNet.Identity.IUser
    where TKey : global::System.IEquatable
{
    //...

    public ITokenProvider PasswordResetTokens { get; set; }
    public virtual Task GetPasswordResetTokenAsync(TKey userId);
    public virtual Task ResetPasswordAsync(TKey userId, string token, string newPassword);
    
    //...
}
    

After taking some time with the disassembled code, what we need is quite simple. We need an implementation of ITokenProvider and set it as the PasswordResetTokens of the UserManager instance that we are using. I am doing this at the AccountController when initializing the UserManager. Here is an example

[RoutePrefix("api/accounts")]
public class AccountController : ApiControllerBase
{
    //...

        AccountController()
    {
        //...
        
        UserManager.PasswordResetTokens = new DataProtectorTokenProvider(Startup.ResetPasswordDataProtectorFactory());
    }
    
    //...
}
        

After this we can:

  1. use the method GetPasswordResetTokenAsync to get the token to send by email to the user, allowing him to reset the password
  2. validate the token using UserManager.PasswordresetTokens.Validate. In case of success we get an instance of the class UserToken which has the property UserId
  3. and finally, we can use the ResetPasswordAsync to reset the password

 

So, basically we need an implementation of an ITokenProvider. We can use whatever we want to generate and validate the token, but we can't forget that we are in the middle of a security problem, so it's obvious that we have to be carefull with our implementation. In the example above, I am using the DataProtectorTokenProvider, available in the nuget Microsoft AspNet Identity Owin (the Owin implementation for ASP.NET identity. You can read more about Owin here). This is a generic implementation, which receives an instance of IDataProtector (defined in Microsoft.Owin.Security package)

namespace Microsoft.Owin.Security.DataProtection
{
    // Summary:
    //     Service used to protect and unprotect data
    public interface IDataProtector
    {
        // Summary:
        //     Called to protect user data.
        //
        // Parameters:
        //   userData:
        //     The original data that must be protected
        //
        // Returns:
        //     A different byte array that may be unprotected or altered only by software
        //     that has access to the an identical IDataProtection service.
        byte[] Protect(byte[] userData);
        //
        // Summary:
        //     Called to unprotect user data
        //
        // Parameters:
        //   protectedData:
        //     The byte array returned by a call to Protect on an identical IDataProtection
        //     service.
        //
        // Returns:
        //     The byte array identical to the original userData passed to Protect.
        byte[] Unprotect(byte[] protectedData);
    }
}

In the examle above, I am using the intrastructure provided by Owin Security, which uses Windows Data Protection API. In the Owin Startup class, I define the data protector factory.

public partial class Startup
{
    //...
    internal static Func<IDataProtector> ResetPasswordDataProtectorFactory;

    public void ConfigureAuth(IAppBuilder app)
    {
        //...
    
        ResetPasswordDataProtectorFactory = () => app.CreateDataProtector("ResetPassword");
        
        //...

    }
}

The app.CreateDataProtector uses a DpapiDataProtector.

As a conclusion, I was very surprised by the fact Microsoft decided to not include some features in ASP.NET Identity 1.0, such as password recovery. However, I am very glad to know in advance they are working on a new version 1.1 version, which apparently will include some of the features. For now, you can take the nightly builds to get the features (on your own risk) or implement it by hand.

Published Dec 4, 2013

Cloud Solutions and Software Engineer. Married and father of two sons. Obsessed to be a continuous learner.